JavaScript Object Model, Browser APIs & Secure Interop Contracts
A runnable Wisej.NET 4 example: an advanced keyboard command palette (Ctrl+K, written in JavaScript and served from the project folder) and a browser capability panel. JavaScript collects browser features and triggers server-side commands through a typed interop contract — and the server enforces permissions before the command runs.
Every boundary is a small API. The palette suggests; the server decides.
Run it
cd EnterpriseOps
dotnet run -f net10.0 --urls http://localhost:5209
Then open http://localhost:5209. In-memory data only — no database, no network, no cloud account.
What you are looking at
The CommandCenterShell page, as in the walkthrough:
| Control | What it shows |
|---|---|
header lblScreenName | EnterpriseOps — Command Center |
commandPaletteHost | the Wisej.Web.Widget that loads /Interop/palette.client.js (Package) and command-palette-host.js (InitScript). Its resting card shows the hotkey and the last command sent. Press Ctrl+K anywhere on the page. |
capabilityPanel | Browser capabilities · detection only: one ✓/✕ row per capability, with the fallback the server chose for anything missing. It never grants a permission. |
lblBanner | the failure banner (hidden until a command is refused) |
lblStatus | the status bar at the bottom |
The session is signed in as ben.tech (Technician, tenant contoso) — there is no login screen.
The palette targets WO-1040, the first work order of the queue.
What to try
| In the palette (Ctrl+K) | Path | What you should see |
|---|---|---|
| approve → Enter | failure — the path | banner You can't approve work order. · PERMISSION_DENIED · checked server-side in ClientCommandService · the command never ran; status bar CommandResult.Fail — PERMISSION_DENIED · audited · correlation …; the palette stays open with the code in its footer |
| escalate → Enter | success | the one state change a Technician holds: toast Escalated (v2)., the palette closes, status bar OK · … |
| escalate again | failure | INVALID_STATE — it cannot be escalated again |
| open work queue → Enter | success | a command with no entity |
Rows the role does not hold are greyed with needs a higher role — a display hint only; running one
still goes to the server, which answers PERMISSION_DENIED. Every crossing is logged server-side
(Client: → Interop: → Security: → Service: → Data: → Audit:) through
System.Diagnostics.Trace.
File tree
Module 9/
README.md
EnterpriseOps.slnx
EnterpriseOps/
EnterpriseOps.csproj (net10.0-windows;net10.0 · embeds the InitScript)
Program.cs Startup.cs Default.html Default.json Web.config
Properties/launchSettings.json (http://localhost:5209)
Interop/
JavaScriptInteropContractPatterns.cs ClientCommandRequest · ClientCommandResult ·
CommandDescriptor · InteropContract · IClientCommandService
CommandPaletteHost.cs the Widget: Package + InitScript + WiredEvents,
[WebMethod] GetCommandCatalog (RegisterWebMethods),
lifecycle-guarded server → client calls
palette.client.js the palette library (Package, served statically)
command-palette-host.js the client adapter (InitScript, embedded resource)
UI/
CommandCenterShell.cs / .Designer.cs the page + the RunClientCommand [WebMethod]
BrowserCapabilityPanel.cs / .Designer.cs
Services/
ActivityTrace.cs server-side log (System.Diagnostics.Trace)
SessionContext.cs SessionContext · CommandContext · CommandResult ·
ResultCodes · ServiceRegistry
ClientCommandService.cs the owner of the boundary — five gates
WorkOrderService.cs the business rules and the WorkQueueRow projection
BrowserCapabilityService.cs accepts, sanitises and drops; picks the fallbacks
Security/
AppUser.cs Permission.cs PermissionService.cs AuditLog.cs
Domain/WorkOrder.cs
Data/InMemoryWorkOrderRepository.cs ~60 rows across three tenants, deterministic seed
docs/
InteropContract.md interop-boundary.svg
CommandPaletteScript.md
ServerCallbackMethods.md
BrowserCapabilityPanel.md
SecurityReviewNotes.md