sh3-core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sh3-core might be problematic. Click here for more details.

Files changed (36) hide show
  1. package/dist/api.d.ts +1 -0
  2. package/dist/assets/icons.svg +1119 -1119
  3. package/dist/auth/auth.svelte.d.ts +6 -0
  4. package/dist/auth/auth.svelte.js +8 -0
  5. package/dist/auth/index.d.ts +1 -1
  6. package/dist/auth/index.js +1 -1
  7. package/dist/createShell.d.ts +31 -0
  8. package/dist/createShell.js +109 -0
  9. package/dist/diagnostic/DiagnosticPanel.svelte +106 -0
  10. package/dist/diagnostic/DiagnosticPanel.svelte.d.ts +3 -0
  11. package/dist/diagnostic/DiagnosticPromptModal.svelte +82 -0
  12. package/dist/diagnostic/DiagnosticPromptModal.svelte.d.ts +8 -0
  13. package/dist/diagnostic/diagnosticApp.d.ts +2 -0
  14. package/dist/diagnostic/diagnosticApp.js +24 -0
  15. package/dist/diagnostic/diagnosticShard.svelte.d.ts +2 -0
  16. package/dist/diagnostic/diagnosticShard.svelte.js +106 -0
  17. package/dist/host-entry.d.ts +4 -1
  18. package/dist/host-entry.js +3 -1
  19. package/dist/host.d.ts +10 -1
  20. package/dist/host.js +34 -22
  21. package/dist/platform/index.d.ts +10 -0
  22. package/dist/platform/index.js +37 -0
  23. package/dist/platform/tauri-backend.d.ts +15 -0
  24. package/dist/platform/tauri-backend.js +58 -0
  25. package/dist/registry/schema.js +5 -0
  26. package/dist/registry/types.d.ts +20 -3
  27. package/dist/server-shard/types.d.ts +67 -0
  28. package/dist/server-shard/types.js +13 -0
  29. package/dist/shards/types.d.ts +8 -0
  30. package/package.json +1 -1
  31. package/dist/registry-shard/RegistryView.svelte +0 -561
  32. package/dist/registry-shard/RegistryView.svelte.d.ts +0 -3
  33. package/dist/registry-shard/registryApp.d.ts +0 -10
  34. package/dist/registry-shard/registryApp.js +0 -24
  35. package/dist/registry-shard/registryShard.svelte.d.ts +0 -45
  36. package/dist/registry-shard/registryShard.svelte.js +0 -125
@@ -37,6 +37,12 @@ export declare function deescalate(): void;
37
37
  * Reactive getter — true when the user has elevated to admin mode.
38
38
  */
39
39
  export declare function isAdmin(): boolean;
40
+ /**
41
+ * Mark this session as local-owner — auto-elevate to admin without
42
+ * key verification. Called by the host in Tauri / dev environments
43
+ * where the user owns the machine. Idempotent.
44
+ */
45
+ export declare function setLocalOwner(): void;
40
46
  /**
41
47
  * Build an Authorization header value for authenticated fetch calls.
42
48
  * Returns null if not elevated.
@@ -109,6 +109,14 @@ export function deescalate() {
109
109
  export function isAdmin() {
110
110
  return admin;
111
111
  }
112
+ /**
113
+ * Mark this session as local-owner — auto-elevate to admin without
114
+ * key verification. Called by the host in Tauri / dev environments
115
+ * where the user owns the machine. Idempotent.
116
+ */
117
+ export function setLocalOwner() {
118
+ admin = true;
119
+ }
112
120
  /**
113
121
  * Build an Authorization header value for authenticated fetch calls.
114
122
  * Returns null if not elevated.
@@ -1 +1 @@
1
- export { initAuth, elevate, deescalate, isAdmin, getAuthHeader } from './auth.svelte';
1
+ export { initAuth, elevate, deescalate, isAdmin, getAuthHeader, setLocalOwner } from './auth.svelte';
@@ -1 +1 @@
1
- export { initAuth, elevate, deescalate, isAdmin, getAuthHeader } from './auth.svelte';
1
+ export { initAuth, elevate, deescalate, isAdmin, getAuthHeader, setLocalOwner } from './auth.svelte';
@@ -0,0 +1,31 @@
1
+ import type { Shard, App } from './index';
2
+ export interface ShellConfig {
3
+ /** Framework shard IDs to exclude (all included by default) */
4
+ excludeShards?: string[];
5
+ /** Framework app IDs to exclude (all included by default) */
6
+ excludeApps?: string[];
7
+ /** Additional shards to register */
8
+ shards?: Shard[];
9
+ /** Additional apps to register */
10
+ apps?: App[];
11
+ /**
12
+ * Packages to pre-install at boot if not already present in IndexedDB.
13
+ * Used for environments that ship certain packages out of the box
14
+ * (e.g. a registry host pre-includes sh3-registry).
15
+ */
16
+ preinstall?: Array<{
17
+ /** Path to the client bundle file (relative to host root or absolute URL). */
18
+ path: string;
19
+ /** Path to the server bundle file (optional, for shards with server counterparts). */
20
+ serverPath?: string;
21
+ /** Package metadata for the install record. */
22
+ meta: {
23
+ id: string;
24
+ type: 'shard' | 'app';
25
+ version: string;
26
+ };
27
+ }>;
28
+ /** Mount target — CSS selector or element (defaults to '#app') */
29
+ target?: string | HTMLElement;
30
+ }
31
+ export declare function createShell(config?: ShellConfig): Promise<void>;
@@ -0,0 +1,109 @@
1
+ /*
2
+ * createShell — public factory for booting an SH3 shell.
3
+ *
4
+ * Consumers call this from their own main.ts instead of manually
5
+ * importing registerShard / registerApp / bootstrap / Shell. The
6
+ * factory handles platform detection, registration, bootstrap, and
7
+ * mounting in the correct order.
8
+ */
9
+ import { mount } from 'svelte';
10
+ import { Shell } from './index';
11
+ import { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner, installPackage, listInstalledPackages, } from './host';
12
+ import { resolvePlatform } from './platform/index';
13
+ export async function createShell(config) {
14
+ var _a, _b;
15
+ // 1. Platform detection — must run before bootstrap so state zones
16
+ // hydrate from the right backend, and local-owner environments
17
+ // auto-elevate to admin.
18
+ const platform = await resolvePlatform();
19
+ if (platform.backends) {
20
+ __setBackend('workspace', platform.backends.workspace);
21
+ __setBackend('user', platform.backends.user);
22
+ }
23
+ if (platform.localOwner) {
24
+ setLocalOwner();
25
+ }
26
+ // 1b. Pre-install packages that this host ships out of the box.
27
+ if ((_a = config === null || config === void 0 ? void 0 : config.preinstall) === null || _a === void 0 ? void 0 : _a.length) {
28
+ const installed = await listInstalledPackages();
29
+ const installedIds = new Set(installed.map((p) => p.id));
30
+ for (const pkg of config.preinstall) {
31
+ if (installedIds.has(pkg.meta.id))
32
+ continue;
33
+ try {
34
+ const res = await fetch(pkg.path);
35
+ if (!res.ok) {
36
+ console.warn(`[sh3] Pre-install fetch failed for "${pkg.meta.id}": HTTP ${res.status}`);
37
+ continue;
38
+ }
39
+ const bundle = await res.arrayBuffer();
40
+ const result = await installPackage(bundle, {
41
+ id: pkg.meta.id,
42
+ type: pkg.meta.type,
43
+ version: pkg.meta.version,
44
+ contractVersion: '1',
45
+ sourceRegistry: 'local',
46
+ integrity: '',
47
+ hasServerBundle: !!pkg.serverPath,
48
+ });
49
+ if (!result.success) {
50
+ console.warn(`[sh3] Pre-install failed for "${pkg.meta.id}":`, result.error);
51
+ continue;
52
+ }
53
+ // Push server bundle if present
54
+ if (pkg.serverPath) {
55
+ try {
56
+ const serverRes = await fetch(pkg.serverPath);
57
+ if (serverRes.ok) {
58
+ const serverBytes = await serverRes.arrayBuffer();
59
+ const form = new FormData();
60
+ form.append('shardId', pkg.meta.id);
61
+ form.append('bundle', new Blob([serverBytes], { type: 'application/javascript' }), 'bundle.js');
62
+ form.append('manifest', JSON.stringify(pkg.meta));
63
+ await fetch('/api/server-shards/install', {
64
+ method: 'POST',
65
+ body: form,
66
+ });
67
+ }
68
+ }
69
+ catch (_c) {
70
+ console.warn(`[sh3] Server bundle push failed for pre-install "${pkg.meta.id}"`);
71
+ }
72
+ }
73
+ console.log(`[sh3] Pre-installed: ${pkg.meta.id}`);
74
+ }
75
+ catch (err) {
76
+ console.warn(`[sh3] Pre-install error for "${pkg.meta.id}":`, err instanceof Error ? err.message : err);
77
+ }
78
+ }
79
+ }
80
+ // 2. Register consumer-provided shards and apps. These go in before
81
+ // bootstrap() so they appear in registeredShards, but framework
82
+ // shards activate first (insertion-order guarantee in bootstrap).
83
+ if (config === null || config === void 0 ? void 0 : config.shards) {
84
+ for (const shard of config.shards) {
85
+ registerShard(shard);
86
+ }
87
+ }
88
+ if (config === null || config === void 0 ? void 0 : config.apps) {
89
+ for (const app of config.apps) {
90
+ registerApp(app);
91
+ }
92
+ }
93
+ // 3. Bootstrap — framework shards/apps registered internally,
94
+ // filtered by the exclude lists.
95
+ const bootstrapConfig = {};
96
+ if (config === null || config === void 0 ? void 0 : config.excludeShards)
97
+ bootstrapConfig.excludeShards = config.excludeShards;
98
+ if (config === null || config === void 0 ? void 0 : config.excludeApps)
99
+ bootstrapConfig.excludeApps = config.excludeApps;
100
+ await bootstrap(bootstrapConfig);
101
+ // 4. Mount the shell.
102
+ const target = typeof (config === null || config === void 0 ? void 0 : config.target) === 'string'
103
+ ? document.querySelector(config.target)
104
+ : (_b = config === null || config === void 0 ? void 0 : config.target) !== null && _b !== void 0 ? _b : document.getElementById('app');
105
+ if (!target) {
106
+ throw new Error('SH3: mount target not found');
107
+ }
108
+ mount(Shell, { target });
109
+ }
@@ -0,0 +1,106 @@
1
+ <script lang="ts">
2
+ /*
3
+ * Diagnostic panel — framework introspection view.
4
+ *
5
+ * Shows:
6
+ * - Registered shards (id, version, view count)
7
+ * - Active shards (id)
8
+ * - Active app (id) or "none"
9
+ * - Active layout source (home vs app)
10
+ *
11
+ * Reads through the public sh3 API surface only.
12
+ */
13
+
14
+ import {
15
+ listRegisteredApps,
16
+ getActiveApp,
17
+ inspectActiveLayout,
18
+ registeredShards,
19
+ activeShards,
20
+ } from '../api';
21
+
22
+ const apps = $derived(listRegisteredApps());
23
+ const active = $derived(getActiveApp());
24
+ const layout = $derived(inspectActiveLayout());
25
+ const regShards = $derived(Array.from(registeredShards.values()));
26
+ const actShards = $derived(Array.from(activeShards.keys()));
27
+ </script>
28
+
29
+ <div class="diagnostic">
30
+ <h2>Diagnostic</h2>
31
+
32
+ <section>
33
+ <h3>Active app</h3>
34
+ <p>{active ? `${active.label} (${active.id})` : 'none'}</p>
35
+ </section>
36
+
37
+ <section>
38
+ <h3>Layout source</h3>
39
+ <p>{layout.source}</p>
40
+ </section>
41
+
42
+ <section>
43
+ <h3>Registered shards ({regShards.length})</h3>
44
+ <ul>
45
+ {#each regShards as shard (shard.manifest.id)}
46
+ <li>
47
+ {shard.manifest.label} — {shard.manifest.id} — v{shard.manifest.version}
48
+ — {shard.manifest.views.length} views
49
+ </li>
50
+ {/each}
51
+ </ul>
52
+ </section>
53
+
54
+ <section>
55
+ <h3>Active shards ({actShards.length})</h3>
56
+ <ul>
57
+ {#each actShards as id (id)}
58
+ <li>{id}</li>
59
+ {/each}
60
+ </ul>
61
+ </section>
62
+
63
+ <section>
64
+ <h3>Registered apps ({apps.length})</h3>
65
+ <ul>
66
+ {#each apps as manifest (manifest.id)}
67
+ <li>{manifest.label} — {manifest.id} — v{manifest.version}</li>
68
+ {/each}
69
+ </ul>
70
+ </section>
71
+ </div>
72
+
73
+ <style>
74
+ .diagnostic {
75
+ position: absolute;
76
+ inset: 0;
77
+ padding: 12px 16px;
78
+ overflow: auto;
79
+ background: var(--shell-bg);
80
+ color: var(--shell-fg);
81
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
82
+ font-size: 12px;
83
+ }
84
+ h2 {
85
+ margin: 0 0 12px;
86
+ color: var(--shell-accent);
87
+ font-size: 14px;
88
+ }
89
+ h3 {
90
+ margin: 12px 0 4px;
91
+ color: var(--shell-fg-muted);
92
+ font-size: 11px;
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.5px;
95
+ }
96
+ p, li {
97
+ margin: 0;
98
+ }
99
+ ul {
100
+ margin: 0;
101
+ padding-left: 16px;
102
+ }
103
+ section {
104
+ margin-bottom: 8px;
105
+ }
106
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const DiagnosticPanel: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type DiagnosticPanel = ReturnType<typeof DiagnosticPanel>;
3
+ export default DiagnosticPanel;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ /*
3
+ * DiagnosticPromptModal — first-time prompt asking the user whether the
4
+ * diagnostic panel should dock into the current app's layout or stay
5
+ * silent for this session. The choice is persisted per-app-id in the
6
+ * diagnostic shard's workspace zone.
7
+ *
8
+ * Props:
9
+ * - appLabel: human label of the active app (for the prompt text)
10
+ * - onChoose: callback invoked with the user's choice; also responsible
11
+ * for any side effects (persist + dock). We call `close()` right
12
+ * after so the modal tears down regardless.
13
+ *
14
+ * The `close` prop is injected by the modal manager (see overlays/modal.ts).
15
+ */
16
+
17
+ let {
18
+ appLabel,
19
+ onChoose,
20
+ close,
21
+ }: {
22
+ appLabel: string;
23
+ onChoose: (choice: 'dock' | 'silent') => void;
24
+ close: () => void;
25
+ } = $props();
26
+
27
+ function pick(choice: 'dock' | 'silent') {
28
+ onChoose(choice);
29
+ close();
30
+ }
31
+ </script>
32
+
33
+ <div class="body">
34
+ <h2>Diagnostic shard</h2>
35
+ <p>
36
+ Dock the diagnostic panel into <strong>{appLabel}</strong>'s layout,
37
+ or keep it silent for this session? Your choice is remembered per app.
38
+ </p>
39
+ <div class="row">
40
+ <button type="button" onclick={() => pick('dock')}>Dock</button>
41
+ <button type="button" class="secondary" onclick={() => pick('silent')}>Silent</button>
42
+ </div>
43
+ </div>
44
+
45
+ <style>
46
+ .body {
47
+ padding: var(--shell-pad-lg);
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--shell-pad-md);
51
+ min-width: 320px;
52
+ }
53
+ h2 {
54
+ margin: 0;
55
+ font-size: 16px;
56
+ color: var(--shell-fg);
57
+ }
58
+ p {
59
+ margin: 0;
60
+ color: var(--shell-fg-muted);
61
+ font-size: 13px;
62
+ line-height: 1.5;
63
+ }
64
+ .row {
65
+ display: flex;
66
+ gap: var(--shell-pad-sm);
67
+ }
68
+ button {
69
+ appearance: none;
70
+ font: inherit;
71
+ font-size: 12px;
72
+ padding: var(--shell-pad-sm) var(--shell-pad-md);
73
+ background: var(--shell-accent-muted);
74
+ color: var(--shell-fg);
75
+ border: 1px solid var(--shell-border-strong);
76
+ border-radius: 3px;
77
+ cursor: pointer;
78
+ }
79
+ button:hover { background: var(--shell-accent); }
80
+ button.secondary { background: transparent; }
81
+ button.secondary:hover { background: var(--shell-bg-sunken); }
82
+ </style>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ appLabel: string;
3
+ onChoose: (choice: 'dock' | 'silent') => void;
4
+ close: () => void;
5
+ };
6
+ declare const DiagnosticPromptModal: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type DiagnosticPromptModal = ReturnType<typeof DiagnosticPromptModal>;
8
+ export default DiagnosticPromptModal;
@@ -0,0 +1,2 @@
1
+ import type { App } from '../apps/types';
2
+ export declare const diagnosticApp: App;
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Diagnostic app — framework-shipped introspection app.
3
+ *
4
+ * Provides a launchable app from the home screen that opens the
5
+ * diagnostic panel in a single-tab layout. Unlike the shard's
6
+ * autostart dock behavior (which splices into other apps), this
7
+ * gives the diagnostic view its own dedicated workspace.
8
+ */
9
+ export const diagnosticApp = {
10
+ manifest: {
11
+ id: 'diagnostic-app',
12
+ label: 'Diagnostic',
13
+ version: '0.1.0',
14
+ requiredShards: ['diagnostic'],
15
+ layoutVersion: 1,
16
+ },
17
+ initialLayout: {
18
+ type: 'tabs',
19
+ activeTab: 0,
20
+ tabs: [
21
+ { slotId: 'diagnostic.main', viewId: 'diagnostic:panel', label: 'Diagnostic' },
22
+ ],
23
+ },
24
+ };
@@ -0,0 +1,2 @@
1
+ import type { Shard } from '../api';
2
+ export declare const diagnosticShard: Shard;
@@ -0,0 +1,106 @@
1
+ /*
2
+ * Diagnostic shard — self-driving framework introspection shard.
3
+ *
4
+ * Always loaded in phase 8 (no env split yet). Self-starts via its
5
+ * `autostart` hook:
6
+ * - If the shell is on home at boot: silently attempt to splice a
7
+ * diagnostic panel into home's layout. Home is a single-slot layout
8
+ * in phase 8, so this gracefully no-ops when no tabs group is found.
9
+ * - If an app is active: read own workspace zone for a per-app
10
+ * `sessionBehavior` preference. If 'silent', do nothing. If 'dock',
11
+ * attempt dock. If unset, open a modal asking "Dock" vs "Silent".
12
+ * Persist the choice and act on it.
13
+ *
14
+ * Note on internal imports: DiagnosticPanel.svelte reads from
15
+ * `shards/activate.svelte.ts` directly for the registeredShards /
16
+ * activeShards reactive maps. That's a deliberate phase-8 shortcut —
17
+ * introspection helpers belong on the public api.ts surface long-term,
18
+ * but factoring them out is a phase-9 concern.
19
+ *
20
+ * Note on the modal path: in phase 8 all shards load at boot, so
21
+ * autostart runs while the shell is on home. The per-app modal prompt
22
+ * path is therefore effectively dead code in phase 8 (it would only be
23
+ * reachable if diagnostic were activated mid-session after an app had
24
+ * launched). Included for design completeness.
25
+ */
26
+ import { mount, unmount } from 'svelte';
27
+ import DiagnosticPanel from './DiagnosticPanel.svelte';
28
+ import DiagnosticPromptModal from './DiagnosticPromptModal.svelte';
29
+ import { shell, getActiveApp, spliceIntoActiveLayout, inspectActiveLayout, } from '../api';
30
+ export const diagnosticShard = {
31
+ manifest: {
32
+ id: 'diagnostic',
33
+ label: 'Diagnostic',
34
+ version: '0.1.0',
35
+ views: [{ id: 'diagnostic:panel', label: 'Diagnostic' }],
36
+ },
37
+ activate(ctx) {
38
+ const factory = {
39
+ mount(container, _context) {
40
+ const instance = mount(DiagnosticPanel, { target: container });
41
+ return {
42
+ unmount() {
43
+ unmount(instance);
44
+ },
45
+ };
46
+ },
47
+ };
48
+ ctx.registerView('diagnostic:panel', factory);
49
+ },
50
+ autostart(ctx) {
51
+ const state = ctx.state({
52
+ workspace: {
53
+ sessionBehavior: {},
54
+ },
55
+ });
56
+ const active = getActiveApp();
57
+ // Home context: attempt a silent dock. Home's single-slot layout has
58
+ // no tabs group in phase 8 so tryDock will no-op gracefully.
59
+ if (!active) {
60
+ tryDock();
61
+ return;
62
+ }
63
+ const contextKey = active.id;
64
+ const stored = state.workspace.sessionBehavior[contextKey];
65
+ if (stored === 'silent')
66
+ return;
67
+ if (stored === 'dock') {
68
+ tryDock();
69
+ return;
70
+ }
71
+ // First-time prompt for this app.
72
+ shell.modal.open(DiagnosticPromptModal, {
73
+ appLabel: active.label,
74
+ onChoose: (choice) => {
75
+ state.workspace.sessionBehavior[contextKey] = choice;
76
+ if (choice === 'dock')
77
+ tryDock();
78
+ },
79
+ });
80
+ },
81
+ };
82
+ function tryDock() {
83
+ const { root } = inspectActiveLayout();
84
+ if (!containsTabs(root))
85
+ return;
86
+ try {
87
+ spliceIntoActiveLayout({
88
+ slotId: 'diagnostic.panel',
89
+ viewId: 'diagnostic:panel',
90
+ label: 'Diagnostic',
91
+ });
92
+ }
93
+ catch (err) {
94
+ // Splice can still refuse for reasons the containsTabs probe doesn't
95
+ // cover (e.g. a layout shape the helper doesn't accept). Failing
96
+ // silently is the right call for a self-starting introspection shard.
97
+ console.warn('[diagnostic] splice failed:', err);
98
+ }
99
+ }
100
+ function containsTabs(node) {
101
+ if (node.type === 'tabs')
102
+ return true;
103
+ if (node.type === 'split')
104
+ return node.children.some((c) => containsTabs(c));
105
+ return false;
106
+ }
@@ -1,4 +1,5 @@
1
- export { registerShard, registerApp, bootstrap, __setBackend } from './host';
1
+ export { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner } from './host';
2
+ export type { BootstrapConfig } from './host';
2
3
  export { __setTenantId, __setDocumentBackend } from './host';
3
4
  export type { Backend } from './state/types';
4
5
  export type { DocumentBackend } from './documents/types';
@@ -7,3 +8,5 @@ export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledP
7
8
  export type { InstalledPackage, InstallResult, PackageMeta } from './registry/types';
8
9
  export { initAuth, elevate, deescalate } from './auth/index';
9
10
  export { adminAppIds } from './host';
11
+ export { createShell } from './createShell';
12
+ export type { ShellConfig } from './createShell';
@@ -5,7 +5,7 @@
5
5
  * boots an SH3 shell (a main.ts that mounts Shell and registers shards/apps)
6
6
  * should touch this path. Shards and apps must not import from here.
7
7
  */
8
- export { registerShard, registerApp, bootstrap, __setBackend } from './host';
8
+ export { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner } from './host';
9
9
  export { __setTenantId, __setDocumentBackend } from './host';
10
10
  export { HttpDocumentBackend } from './documents/http-backend';
11
11
  // Install API (host-only).
@@ -13,3 +13,5 @@ export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledP
13
13
  // Admin mode (host-only — elevate/deescalate drive the shell UI, initAuth runs at boot).
14
14
  export { initAuth, elevate, deescalate } from './auth/index';
15
15
  export { adminAppIds } from './host';
16
+ // Shell boot factory.
17
+ export { createShell } from './createShell';
package/dist/host.d.ts CHANGED
@@ -1,13 +1,22 @@
1
1
  import { registerShard as registerShardInternal } from './shards/activate.svelte';
2
2
  import { registerApp } from './apps/registry.svelte';
3
3
  import { __setBackend } from './state/zones.svelte';
4
+ import { setLocalOwner } from './auth/index';
4
5
  export { __setBackend };
6
+ export { setLocalOwner };
5
7
  export { __setTenantId, __setDocumentBackend } from './documents/config';
6
8
  export declare function registerShard(shard: Parameters<typeof registerShardInternal>[0]): void;
7
9
  export { registerApp };
8
- export declare function bootstrap(): Promise<void>;
10
+ export interface BootstrapConfig {
11
+ /** Framework shard IDs to skip registration for */
12
+ excludeShards?: string[];
13
+ /** Framework app IDs to skip registration for */
14
+ excludeApps?: string[];
15
+ }
16
+ export declare function bootstrap(config?: BootstrapConfig): Promise<void>;
9
17
  /**
10
18
  * Set of app IDs that require admin mode. Framework-internal — used by
11
19
  * the shell home to gate visibility. Not part of the app contract.
12
20
  */
13
21
  export declare const adminAppIds: ReadonlySet<string>;
22
+ export { installPackage, listInstalledPackages } from './registry/installer';
package/dist/host.js CHANGED
@@ -3,13 +3,13 @@
3
3
  *
4
4
  * Registration APIs (`registerShard`, `registerApp`) are data-only and
5
5
  * safe to call at any time; a future runtime loader uses them identically
6
- * to the boot-time glob loop.
6
+ * to hot-install registration at runtime.
7
7
  *
8
8
  * `bootstrap()` runs the post-registration boot sequence: it registers
9
- * the framework-owned __shell__ pseudo-shard, walks the registered-shards
10
- * map and activates every self-starting shard, then reads the last-app
11
- * user-zone entry and either launches that app or leaves the shell on
12
- * home.
9
+ * framework-owned shards and apps (filtering any the host excludes via
10
+ * BootstrapConfig), walks the registered-shards map and activates every
11
+ * self-starting shard, then reads the last-app user-zone entry and
12
+ * either launches that app or leaves the shell on home.
13
13
  *
14
14
  * This file is intentionally NOT re-exported through `api.ts`. The
15
15
  * import-hygiene rule is: shards and apps import from `api.ts`, the host
@@ -22,30 +22,41 @@ import { shellShard } from './shell-shard/shellShard.svelte';
22
22
  import { storeShard } from './store/storeShard.svelte';
23
23
  import { __setBackend } from './state/zones.svelte';
24
24
  import { loadInstalledPackages } from './registry/installer';
25
- import { initAuth } from './auth/index';
25
+ import { initAuth, isAdmin, setLocalOwner } from './auth/index';
26
26
  import { storeApp } from './store/storeApp';
27
- import { registryShard } from './registry-shard/registryShard.svelte';
28
- import { registryApp } from './registry-shard/registryApp';
27
+ import { diagnosticShard } from './diagnostic/diagnosticShard.svelte';
28
+ import { diagnosticApp } from './diagnostic/diagnosticApp';
29
29
  export { __setBackend };
30
+ export { setLocalOwner };
30
31
  export { __setTenantId, __setDocumentBackend } from './documents/config';
31
32
  export function registerShard(shard) {
32
33
  registerShardInternal(shard);
33
34
  }
34
35
  export { registerApp };
35
- export async function bootstrap() {
36
- // 1. Framework-owned shards are registered first before installed
37
- // packages so that a malicious installed package cannot claim a
38
- // reserved id like __shell__ or sh3-store.
39
- registerShardInternal(shellShard);
40
- registerShardInternal(storeShard);
41
- registerShardInternal(registryShard);
36
+ export async function bootstrap(config) {
37
+ const exShards = new Set(config === null || config === void 0 ? void 0 : config.excludeShards);
38
+ const exApps = new Set(config === null || config === void 0 ? void 0 : config.excludeApps);
39
+ // 1. Framework-owned shards registered first so installed packages
40
+ // cannot claim reserved IDs like __shell__ or sh3-store.
41
+ const frameworkShards = [shellShard, storeShard, diagnosticShard];
42
+ for (const shard of frameworkShards) {
43
+ if (!exShards.has(shard.manifest.id)) {
44
+ registerShardInternal(shard);
45
+ }
46
+ }
42
47
  // 2. Framework-shipped admin apps.
43
- registerApp(storeApp);
44
- registerApp(registryApp);
45
- // 3. Verify stored admin key (if any) against the server. Uses a
46
- // 3-second timeout and fails open — the shell boots without admin
47
- // access if the server is slow or offline.
48
- await initAuth();
48
+ const frameworkApps = [storeApp, diagnosticApp];
49
+ for (const app of frameworkApps) {
50
+ if (!exApps.has(app.manifest.id)) {
51
+ registerApp(app);
52
+ }
53
+ }
54
+ // 3. Auth — if the host already called setLocalOwner() (Tauri / dev),
55
+ // skip server verification. Otherwise verify the stored admin key
56
+ // against the server (3s timeout, fails open).
57
+ if (!isAdmin()) {
58
+ await initAuth();
59
+ }
49
60
  // 4. Load any packages that were hot-installed in a previous session
50
61
  // from IndexedDB. Runs after framework shards but before activation
51
62
  // so installed packages participate in the self-starting pass.
@@ -70,4 +81,5 @@ export async function bootstrap() {
70
81
  * Set of app IDs that require admin mode. Framework-internal — used by
71
82
  * the shell home to gate visibility. Not part of the app contract.
72
83
  */
73
- export const adminAppIds = new Set(['sh3-store-app', 'sh3-registry-app']);
84
+ export const adminAppIds = new Set(['sh3-store-app', 'diagnostic-app']);
85
+ export { installPackage, listInstalledPackages } from './registry/installer';