sh3-core 0.21.2 → 0.22.1

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.
Files changed (96) hide show
  1. package/dist/__test__/fixtures.js +1 -1
  2. package/dist/__test__/reset.js +1 -1
  3. package/dist/__test__/smoke.test.js +2 -2
  4. package/dist/actions/contextMenuModel.test.js +6 -3
  5. package/dist/actions/ctx-actions.svelte.test.js +9 -9
  6. package/dist/actions/dispatcher-v3.test.js +8 -0
  7. package/dist/actions/dispatcher.svelte.d.ts +1 -2
  8. package/dist/actions/dispatcher.svelte.js +6 -7
  9. package/dist/actions/dispatcher.test.js +9 -12
  10. package/dist/actions/listActionsFromEntries.test.js +1 -2
  11. package/dist/actions/listActive.test.js +2 -3
  12. package/dist/actions/menuBarModel.test.js +1 -7
  13. package/dist/actions/paletteModel.test.js +1 -3
  14. package/dist/actions/scope-helpers.test.js +4 -4
  15. package/dist/actions/shardContext.test.js +2 -2
  16. package/dist/actions/state.svelte.d.ts +12 -2
  17. package/dist/actions/state.svelte.js +15 -12
  18. package/dist/actions/state.test.js +4 -4
  19. package/dist/api.d.ts +3 -3
  20. package/dist/api.js +1 -1
  21. package/dist/app/admin/adminShard.svelte.js +1 -1
  22. package/dist/app/store/storeShard.svelte.js +10 -5
  23. package/dist/app-appearance/appearanceShard.svelte.js +1 -5
  24. package/dist/apps/lifecycle.js +65 -33
  25. package/dist/apps/lifecycle.test.js +198 -10
  26. package/dist/artifact.d.ts +2 -0
  27. package/dist/build.js +1 -1
  28. package/dist/conflicts/adapter-documents.js +1 -2
  29. package/dist/createShell.js +1 -1
  30. package/dist/documents/handle.d.ts +9 -4
  31. package/dist/documents/handle.js +69 -45
  32. package/dist/documents/handle.test.js +99 -27
  33. package/dist/documents/index.d.ts +1 -1
  34. package/dist/documents/types.d.ts +16 -20
  35. package/dist/host.d.ts +1 -1
  36. package/dist/host.js +9 -56
  37. package/dist/host.svelte.test.js +31 -63
  38. package/dist/layout/LayoutRenderer.svelte +1 -1
  39. package/dist/layout/SlotContainer.svelte +1 -0
  40. package/dist/layout/inspection.js +19 -14
  41. package/dist/layout/inspection.svelte.test.js +136 -1
  42. package/dist/layout/slotHostPool.svelte.d.ts +2 -1
  43. package/dist/layout/slotHostPool.svelte.js +6 -3
  44. package/dist/layout/slotHostPool.test.js +17 -0
  45. package/dist/layout/store.projectScope.test.js +76 -0
  46. package/dist/layout/store.svelte.d.ts +6 -0
  47. package/dist/layout/store.svelte.js +43 -13
  48. package/dist/layout/tree-walk.d.ts +8 -1
  49. package/dist/layout/tree-walk.js +11 -1
  50. package/dist/layout/tree-walk.test.js +53 -1
  51. package/dist/layout/types.d.ts +27 -0
  52. package/dist/layout/types.test.js +28 -0
  53. package/dist/layouts-shard/LayoutsSection.svelte +1 -1
  54. package/dist/layouts-shard/layoutsShard.svelte.js +2 -5
  55. package/dist/layouts-shard/layoutsShard.svelte.test.js +2 -2
  56. package/dist/overlays/FloatFrame.svelte +4 -1
  57. package/dist/overlays/float.d.ts +7 -1
  58. package/dist/overlays/float.js +4 -0
  59. package/dist/projects-shard/ProjectsSection.svelte +1 -5
  60. package/dist/projects-shard/projectsShard.svelte.js +1 -5
  61. package/dist/registry/installer.js +1 -1
  62. package/dist/registry/loader.d.ts +1 -1
  63. package/dist/registry/loader.js +3 -3
  64. package/dist/registry/permission-descriptions.test.js +2 -2
  65. package/dist/registry/register.js +1 -1
  66. package/dist/registry/register.test.js +1 -1
  67. package/dist/runtime/runVerb-shell.test.js +1 -1
  68. package/dist/runtime/runVerb.js +2 -2
  69. package/dist/runtime/runVerb.test.js +9 -9
  70. package/dist/sh3Api/headless.js +1 -1
  71. package/dist/sh3core-shard/sh3coreShard.svelte.js +1 -6
  72. package/dist/shards/ctx-fetch.test.js +9 -9
  73. package/dist/shards/lifecycle.svelte.d.ts +108 -0
  74. package/dist/shards/lifecycle.svelte.js +551 -0
  75. package/dist/shards/lifecycle.test.js +139 -0
  76. package/dist/shards/types.d.ts +56 -22
  77. package/dist/shell-shard/shellShard.svelte.js +11 -5
  78. package/dist/version.d.ts +1 -1
  79. package/dist/version.js +1 -1
  80. package/package.json +1 -1
  81. package/dist/shards/activate-browse.test.js +0 -120
  82. package/dist/shards/activate-contributions.test.js +0 -141
  83. package/dist/shards/activate-error-isolation.test.js +0 -98
  84. package/dist/shards/activate-fields.svelte.test.d.ts +0 -1
  85. package/dist/shards/activate-fields.svelte.test.js +0 -121
  86. package/dist/shards/activate-on-key-revoked.test.d.ts +0 -1
  87. package/dist/shards/activate-on-key-revoked.test.js +0 -60
  88. package/dist/shards/activate-runtime.test.d.ts +0 -1
  89. package/dist/shards/activate-runtime.test.js +0 -299
  90. package/dist/shards/activate-scopeid.test.d.ts +0 -1
  91. package/dist/shards/activate-scopeid.test.js +0 -21
  92. package/dist/shards/activate.svelte.d.ts +0 -102
  93. package/dist/shards/activate.svelte.js +0 -403
  94. /package/dist/{shards/activate-browse.test.d.ts → actions/dispatcher-v3.test.d.ts} +0 -0
  95. /package/dist/{shards/activate-contributions.test.d.ts → layout/store.projectScope.test.d.ts} +0 -0
  96. /package/dist/shards/{activate-error-isolation.test.d.ts → lifecycle.test.d.ts} +0 -0
@@ -1,102 +0,0 @@
1
- import type { Shard, ShardContext } from './types';
2
- /**
3
- * Reactive registry of every shard known to the host. Keys are shard ids.
4
- * Populated once at boot by the glob-discovery loop in main.ts (through
5
- * `registerShard`); a future runtime loader appends late without touching
6
- * the rest of the framework. Callers that need a live list subscribe via
7
- * Svelte reactivity (e.g. sh3 home listing apps cross-references this
8
- * map to show which shards they require).
9
- */
10
- export declare const registeredShards: Map<string, Shard>;
11
- export declare const activeShards: Map<string, Shard>;
12
- /**
13
- * Reactive map of shard ids that failed to activate. Populated by
14
- * `activateShard`'s catch block; cleared when the shard is successfully
15
- * re-registered or activated. Read-only for shards; intended for diagnostic
16
- * and admin tooling that wants to surface broken shards to the user.
17
- */
18
- export interface ShardErrorEntry {
19
- id: string;
20
- error: unknown;
21
- phase: 'autostart' | 'launch' | 'satellite';
22
- timestamp: number;
23
- }
24
- export declare const erroredShards: Map<string, ShardErrorEntry>;
25
- /** Host-only. Register a callback that resolves whether the current scope
26
- * is a personal tenant or an active project. Wired by createShell after
27
- * bootstrap. Avoids circular dependencies with session-state. */
28
- export declare function __setScopeResolver(resolver: (() => 'tenant' | 'project') | null): void;
29
- /**
30
- * Register (or re-register) a shard with the framework so it can later be
31
- * activated. Records the shard in `registeredShards` but does not run
32
- * `activate` — that happens at app launch (or for self-starting shards).
33
- *
34
- * If a shard with the same id already exists it is silently replaced,
35
- * which is the expected path during package updates. If the old shard was
36
- * active it is deactivated first so the new version can be cleanly
37
- * activated on next launch.
38
- */
39
- export declare function registerShard(shard: Shard): void;
40
- export interface ActivateShardOpts {
41
- /**
42
- * Where this activation was initiated from. Determines the `phase` field
43
- * recorded in `erroredShards` if activation fails. Defaults to 'launch'
44
- * (the common case — required by an app being launched).
45
- */
46
- phase?: 'autostart' | 'launch' | 'satellite';
47
- }
48
- /**
49
- * Activate a registered shard. Builds a `ShardContext`, calls `shard.activate`,
50
- * verifies that every view declared in the manifest received a factory, then
51
- * calls `shard.autostart` if defined. Idempotent — calling on an already-active
52
- * shard is a no-op.
53
- *
54
- * If `shard.activate` throws, partial state (registered views, verbs,
55
- * contributions, document handles, actions, env subscription) is unwound
56
- * and the failure is recorded in `erroredShards` before the error is
57
- * re-thrown. Callers in `host.ts` (autostart loop) and `launchApp`
58
- * (required-shard loop) decide how to react.
59
- *
60
- * @param id - The `ShardManifest.id` of the shard to activate. Must be registered.
61
- * @param opts - Optional. `phase` is recorded in `erroredShards` on failure (default 'launch').
62
- * @throws If the shard is not registered, if `shard.activate` throws, or if a manifest view has no factory after activation.
63
- */
64
- export declare function activateShard(id: string, opts?: ActivateShardOpts): Promise<void>;
65
- /**
66
- * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
67
- * all document handles, unregisters all view factories, and removes the shard
68
- * from `activeShards`. The shard remains in `registeredShards` and can be
69
- * re-activated. No-op if the shard is not currently active.
70
- *
71
- * @param id - The `ShardManifest.id` of the shard to deactivate.
72
- */
73
- export declare function deactivateShard(id: string): void;
74
- /**
75
- * Return true if the shard with the given id is currently active.
76
- *
77
- * @param id - The `ShardManifest.id` to check.
78
- */
79
- export declare function isActive(id: string): boolean;
80
- /**
81
- * Return the ShardContext for an active shard, or undefined if not active.
82
- * Used by lifecycle.ts to pass context to `shard.resume()`.
83
- */
84
- export declare function getShardContext(id: string): ShardContext | undefined;
85
- /**
86
- * Enumerate every view declared as `standalone` across the currently
87
- * active shards. Intended for the `views --standalone` verb and any
88
- * launcher UI that wants to surface "summonable" primitives. Only
89
- * pulls from `activeShards` — registered-but-inactive shards aren't
90
- * ready to mount.
91
- */
92
- export declare function listStandaloneViews(): Array<{
93
- shardId: string;
94
- viewId: string;
95
- label: string;
96
- }>;
97
- /**
98
- * Test-only reset. Tears down any active shard entries (without running
99
- * deactivate hooks — tests should run deactivate explicitly if they care)
100
- * and clears both registered and active maps.
101
- */
102
- export declare function __resetShardRegistryForTest(): void;
@@ -1,403 +0,0 @@
1
- /*
2
- * Shard lifecycle — phase 8 split into register and activate.
3
- *
4
- * registerShard(shard): adds the shard's manifest to the reactive
5
- * `registeredShards` map. No side effects. The host (main.ts) calls
6
- * this once per shard via its glob-discovery boot loop. A future
7
- * runtime loader calls it after fetching a shard module.
8
- *
9
- * activateShard(id): looks up a registered shard, builds its
10
- * ShardContext, runs `activate(ctx)`, verifies every manifest view id
11
- * received a factory, then runs `autostart(ctx)` if present. The shard
12
- * moves into the reactive `activeShards` map. Called internally by
13
- * app launch and by the bootstrap self-starting pass. Not public.
14
- *
15
- * deactivateShard(id): inverse of activate. Calls `deactivate()`,
16
- * unregisters view factories, drops from `activeShards`. The shard
17
- * stays in `registeredShards` — it's still known, just not running.
18
- */
19
- import { sh3 } from '../sh3Runtime.svelte';
20
- import { registerView, unregisterView, registerVerb as fwRegisterVerb, unregisterVerb as fwUnregisterVerb } from './registry';
21
- import { makeSh3Api } from '../sh3Api/headless';
22
- import { createDocumentHandle, getDocumentBackend, getActiveScopeId } from '../documents';
23
- import { fetchEnvState, putEnvState } from '../env/client';
24
- import { getEnvServerUrl } from '../env/index';
25
- import { apiFetch } from '../transport/apiFetch';
26
- import { isAdmin as checkIsAdmin } from '../auth/index';
27
- import { createZoneManager } from '../state/manage';
28
- import { PERMISSION_STATE_MANAGE } from '../state/types';
29
- import { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from '../documents/types';
30
- import { createBrowseCapability } from '../documents/browse';
31
- import { createDocumentPicker } from '../documents/picker-primitive';
32
- import { createShardKeysApi } from '../keys/client';
33
- import { PERMISSION_KEYS_MINT } from '../keys/types';
34
- import { subscribe } from '../keys/revocation-bus.svelte';
35
- import { register as contributionsRegister, list as contributionsList, listPoints as contributionsListPoints, onChange as contributionsOnChange, onAnyChange as contributionsOnAnyChange, } from '../contributions';
36
- import { registerAction } from '../actions/registry';
37
- import { makeSelectionApi, clearSelectionForShard } from '../actions/selection.svelte';
38
- import { openContextMenu as sh3OpenContextMenu, openPalette as sh3OpenPalette, } from '../actions/listeners';
39
- /**
40
- * Reactive registry of every shard known to the host. Keys are shard ids.
41
- * Populated once at boot by the glob-discovery loop in main.ts (through
42
- * `registerShard`); a future runtime loader appends late without touching
43
- * the rest of the framework. Callers that need a live list subscribe via
44
- * Svelte reactivity (e.g. sh3 home listing apps cross-references this
45
- * map to show which shards they require).
46
- */
47
- export const registeredShards = $state(new Map());
48
- /**
49
- * Reactive map of currently-active shards. A shard lands here when
50
- * `activateShard` runs successfully and stays until `deactivateShard`.
51
- */
52
- const active = new Map();
53
- export const activeShards = $state(new Map());
54
- export const erroredShards = $state(new Map());
55
- let scopeResolver = null;
56
- /** Host-only. Register a callback that resolves whether the current scope
57
- * is a personal tenant or an active project. Wired by createShell after
58
- * bootstrap. Avoids circular dependencies with session-state. */
59
- export function __setScopeResolver(resolver) {
60
- scopeResolver = resolver;
61
- }
62
- /**
63
- * Register (or re-register) a shard with the framework so it can later be
64
- * activated. Records the shard in `registeredShards` but does not run
65
- * `activate` — that happens at app launch (or for self-starting shards).
66
- *
67
- * If a shard with the same id already exists it is silently replaced,
68
- * which is the expected path during package updates. If the old shard was
69
- * active it is deactivated first so the new version can be cleanly
70
- * activated on next launch.
71
- */
72
- export function registerShard(shard) {
73
- const id = shard.manifest.id;
74
- if (registeredShards.has(id) && activeShards.has(id)) {
75
- deactivateShard(id);
76
- }
77
- registeredShards.set(id, shard);
78
- // Re-registering wipes any prior error: the new shard module gets a
79
- // clean slate, and a hot-reload of a fixed shard removes the stale entry.
80
- erroredShards.delete(id);
81
- }
82
- /**
83
- * Activate a registered shard. Builds a `ShardContext`, calls `shard.activate`,
84
- * verifies that every view declared in the manifest received a factory, then
85
- * calls `shard.autostart` if defined. Idempotent — calling on an already-active
86
- * shard is a no-op.
87
- *
88
- * If `shard.activate` throws, partial state (registered views, verbs,
89
- * contributions, document handles, actions, env subscription) is unwound
90
- * and the failure is recorded in `erroredShards` before the error is
91
- * re-thrown. Callers in `host.ts` (autostart loop) and `launchApp`
92
- * (required-shard loop) decide how to react.
93
- *
94
- * @param id - The `ShardManifest.id` of the shard to activate. Must be registered.
95
- * @param opts - Optional. `phase` is recorded in `erroredShards` on failure (default 'launch').
96
- * @throws If the shard is not registered, if `shard.activate` throws, or if a manifest view has no factory after activation.
97
- */
98
- export async function activateShard(id, opts) {
99
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
100
- const shard = registeredShards.get(id);
101
- if (!shard) {
102
- throw new Error(`Cannot activate shard "${id}": not registered`);
103
- }
104
- if (active.has(id)) {
105
- // Already active (e.g. self-starting shard that was activated at boot
106
- // and is now being required by an app). Idempotent — no error.
107
- return;
108
- }
109
- const entry = { shard, ctx: undefined, viewIds: new Set(), verbNames: new Set(), cleanupFns: [] };
110
- // envState holds the reactive env data for this shard.
111
- // Must be declared with $state at variable declaration time (Svelte 5 rule).
112
- const envState = $state({
113
- proxy: null,
114
- defaults: null,
115
- });
116
- // Per-shard wrapper: every register/onChange call goes into the
117
- // global registry, and its disposer is pushed into entry.cleanupFns
118
- // so deactivate auto-unregisters.
119
- const contributions = {
120
- register(pointId, descriptor, contribOpts) {
121
- var _a;
122
- // Auto-wrap controllable-field descriptors with ownership metadata so
123
- // fields/dispatch.ts can route addressed get/set without the user
124
- // descriptor needing to know about the framework's wire format.
125
- let stored = descriptor;
126
- if (pointId === 'sh3.controllable-field') {
127
- const owner = { shardId: id };
128
- if (((_a = contribOpts === null || contribOpts === void 0 ? void 0 : contribOpts.scope) === null || _a === void 0 ? void 0 : _a.slotId) !== undefined) {
129
- owner.slotId = contribOpts.scope.slotId;
130
- }
131
- stored = { owner, descriptor };
132
- }
133
- const dispose = contributionsRegister(pointId, stored, contribOpts);
134
- entry.cleanupFns.push(async () => dispose());
135
- return dispose;
136
- },
137
- list(pointId) {
138
- return contributionsList(pointId);
139
- },
140
- listPoints() {
141
- return contributionsListPoints();
142
- },
143
- onChange(pointId, cb) {
144
- const off = contributionsOnChange(pointId, cb);
145
- entry.cleanupFns.push(async () => off());
146
- return off;
147
- },
148
- onAnyChange(cb) {
149
- const off = contributionsOnAnyChange(cb);
150
- entry.cleanupFns.push(async () => off());
151
- return off;
152
- },
153
- };
154
- const hasBrowse = (_a = shard.manifest.permissions) === null || _a === void 0 ? void 0 : _a.includes(PERMISSION_DOCUMENTS_BROWSE);
155
- const browseCap = hasBrowse
156
- ? createBrowseCapability(getActiveScopeId, getDocumentBackend(), {
157
- canRead: (_c = (_b = shard.manifest.permissions) === null || _b === void 0 ? void 0 : _b.includes(PERMISSION_DOCUMENTS_READ)) !== null && _c !== void 0 ? _c : false,
158
- canWrite: (_e = (_d = shard.manifest.permissions) === null || _d === void 0 ? void 0 : _d.includes(PERMISSION_DOCUMENTS_WRITE)) !== null && _e !== void 0 ? _e : false,
159
- })
160
- : undefined;
161
- const ctx = {
162
- state: (schema) => sh3.state(id, schema),
163
- registerView: (viewId, factory) => {
164
- registerView(viewId, factory, shard.manifest.id);
165
- entry.viewIds.add(viewId);
166
- },
167
- registerVerb: (verb) => {
168
- var _a;
169
- let prefixed;
170
- if (id === 'shell') {
171
- prefixed = verb.name;
172
- }
173
- else {
174
- const ns = (_a = shard.manifest.verbNamespace) !== null && _a !== void 0 ? _a : id;
175
- prefixed = `${ns}:${verb.name}`;
176
- }
177
- if (fwRegisterVerb(prefixed, Object.assign(Object.assign({}, verb), { name: prefixed }), id)) {
178
- entry.verbNames.add(prefixed);
179
- }
180
- },
181
- documents: (options) => {
182
- const handle = createDocumentHandle(getActiveScopeId(), id, getDocumentBackend(), options);
183
- entry.cleanupFns.push(() => handle.dispose());
184
- return handle;
185
- },
186
- fetch(path, init) {
187
- return apiFetch(this.resolveUrl(path), init);
188
- },
189
- get serverUrl() {
190
- return getEnvServerUrl();
191
- },
192
- resolveUrl(path) {
193
- const isAbsolute = path.startsWith('http://') || path.startsWith('https://');
194
- if (isAbsolute)
195
- return path;
196
- const base = getEnvServerUrl();
197
- const sep = path.startsWith('/') ? '' : '/';
198
- return `${base}${sep}${path}`;
199
- },
200
- env(defaults) {
201
- if (envState.proxy) {
202
- console.warn(`[sh3] Shard "${id}" called ctx.env() more than once; extra calls are ignored.`);
203
- return envState.proxy;
204
- }
205
- envState.defaults = defaults;
206
- envState.proxy = Object.assign({}, defaults);
207
- return envState.proxy;
208
- },
209
- async envUpdate(patch) {
210
- if (!envState.proxy || !envState.defaults) {
211
- throw new Error(`Shard "${id}" called envUpdate() without declaring env state`);
212
- }
213
- const previous = $state.snapshot(envState.proxy);
214
- Object.assign(envState.proxy, patch);
215
- try {
216
- const snapshot = $state.snapshot(envState.proxy);
217
- await putEnvState(id, snapshot);
218
- }
219
- catch (err) {
220
- Object.assign(envState.proxy, previous);
221
- throw err;
222
- }
223
- },
224
- get isAdmin() {
225
- return checkIsAdmin();
226
- },
227
- get tenantId() {
228
- return getActiveScopeId();
229
- },
230
- getScope: () => { var _a; return (_a = scopeResolver === null || scopeResolver === void 0 ? void 0 : scopeResolver()) !== null && _a !== void 0 ? _a : 'tenant'; },
231
- zones: ((_f = shard.manifest.permissions) === null || _f === void 0 ? void 0 : _f.includes(PERMISSION_STATE_MANAGE))
232
- ? createZoneManager()
233
- : undefined,
234
- browse: browseCap,
235
- documentPicker: browseCap
236
- ? createDocumentPicker(() => browseCap.listDocuments())
237
- : createDocumentPicker(async () => {
238
- const docs = await getDocumentBackend().list(getActiveScopeId(), id);
239
- return docs.map(d => (Object.assign(Object.assign({}, d), { shardId: id })));
240
- }),
241
- keys: ((_g = shard.manifest.permissions) === null || _g === void 0 ? void 0 : _g.includes(PERMISSION_KEYS_MINT))
242
- ? createShardKeysApi({
243
- shardId: id,
244
- shardPermissions: (_h = shard.manifest.permissions) !== null && _h !== void 0 ? _h : [],
245
- })
246
- : undefined,
247
- contributions,
248
- actions: {
249
- register(action) {
250
- const dispose = registerAction(action, id);
251
- entry.cleanupFns.push(async () => dispose());
252
- return dispose;
253
- },
254
- selection: makeSelectionApi(id),
255
- openContextMenu(opts) { sh3OpenContextMenu(opts); },
256
- openPalette(opts) { sh3OpenPalette(opts); },
257
- },
258
- sh3: makeSh3Api({
259
- callerKind: 'shard',
260
- callerShardId: id,
261
- zones: ((_j = shard.manifest.permissions) === null || _j === void 0 ? void 0 : _j.includes(PERMISSION_STATE_MANAGE))
262
- ? createZoneManager()
263
- : undefined,
264
- }),
265
- };
266
- entry.ctx = ctx;
267
- // Wire onKeyRevoked hook: subscribe to the revocation bus for this shard.
268
- // Only shards that declare the hook incur the subscription overhead.
269
- if (shard.onKeyRevoked) {
270
- const off = subscribe(id, async (keyId) => {
271
- try {
272
- await shard.onKeyRevoked(keyId);
273
- }
274
- catch (err) {
275
- console.error(`[sh3] onKeyRevoked failed in "${id}":`, err);
276
- }
277
- });
278
- entry.cleanupFns.push(async () => off());
279
- }
280
- active.set(id, entry);
281
- activeShards.set(id, shard);
282
- try {
283
- await shard.activate(ctx);
284
- for (const view of shard.manifest.views) {
285
- if (!entry.viewIds.has(view.id)) {
286
- throw new Error(`Shard "${id}" declared view "${view.id}" in its manifest but registered no factory for it.`);
287
- }
288
- }
289
- // Hydrate env state if the shard declared it via ctx.env().
290
- if (envState.proxy && envState.defaults) {
291
- try {
292
- const stored = await fetchEnvState(id);
293
- const merged = Object.assign({}, envState.defaults, stored);
294
- Object.assign(envState.proxy, merged);
295
- }
296
- catch (err) {
297
- console.warn(`[sh3] Failed to hydrate env state for shard "${id}":`, err instanceof Error ? err.message : err);
298
- }
299
- }
300
- }
301
- catch (err) {
302
- // Unwind partial state. Mirror deactivateShard's body, minus the
303
- // shard.deactivate?.() call — the shard never finished activating.
304
- // Each cleanup fn runs inside its own swallow so a teardown failure
305
- // cannot mask the original activation error.
306
- for (const fn of entry.cleanupFns) {
307
- try {
308
- void fn();
309
- }
310
- catch (_m) {
311
- // intentionally swallowed: original error is what matters.
312
- }
313
- }
314
- for (const name of entry.verbNames)
315
- fwUnregisterVerb(name);
316
- for (const viewId of entry.viewIds)
317
- unregisterView(viewId);
318
- clearSelectionForShard(id);
319
- active.delete(id);
320
- activeShards.delete(id);
321
- erroredShards.set(id, {
322
- id,
323
- error: err,
324
- phase: (_k = opts === null || opts === void 0 ? void 0 : opts.phase) !== null && _k !== void 0 ? _k : 'launch',
325
- timestamp: Date.now(),
326
- });
327
- console.error(`[sh3] Shard "${id}" failed to activate:`, err);
328
- throw err;
329
- }
330
- // Activation succeeded — clear any prior error record for this shard.
331
- erroredShards.delete(id);
332
- void ((_l = shard.autostart) === null || _l === void 0 ? void 0 : _l.call(shard, ctx));
333
- }
334
- /**
335
- * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
336
- * all document handles, unregisters all view factories, and removes the shard
337
- * from `activeShards`. The shard remains in `registeredShards` and can be
338
- * re-activated. No-op if the shard is not currently active.
339
- *
340
- * @param id - The `ShardManifest.id` of the shard to deactivate.
341
- */
342
- export function deactivateShard(id) {
343
- var _a, _b;
344
- const entry = active.get(id);
345
- if (!entry)
346
- return;
347
- void ((_b = (_a = entry.shard).deactivate) === null || _b === void 0 ? void 0 : _b.call(_a));
348
- // Flush and dispose document handles before tearing down views.
349
- for (const fn of entry.cleanupFns)
350
- void fn();
351
- for (const name of entry.verbNames)
352
- fwUnregisterVerb(name);
353
- for (const viewId of entry.viewIds)
354
- unregisterView(viewId);
355
- clearSelectionForShard(id);
356
- active.delete(id);
357
- activeShards.delete(id);
358
- }
359
- /**
360
- * Return true if the shard with the given id is currently active.
361
- *
362
- * @param id - The `ShardManifest.id` to check.
363
- */
364
- export function isActive(id) {
365
- return active.has(id);
366
- }
367
- /**
368
- * Return the ShardContext for an active shard, or undefined if not active.
369
- * Used by lifecycle.ts to pass context to `shard.resume()`.
370
- */
371
- export function getShardContext(id) {
372
- var _a;
373
- return (_a = active.get(id)) === null || _a === void 0 ? void 0 : _a.ctx;
374
- }
375
- /**
376
- * Enumerate every view declared as `standalone` across the currently
377
- * active shards. Intended for the `views --standalone` verb and any
378
- * launcher UI that wants to surface "summonable" primitives. Only
379
- * pulls from `activeShards` — registered-but-inactive shards aren't
380
- * ready to mount.
381
- */
382
- export function listStandaloneViews() {
383
- const out = [];
384
- for (const shard of activeShards.values()) {
385
- for (const view of shard.manifest.views) {
386
- if (view.standalone) {
387
- out.push({ shardId: shard.manifest.id, viewId: view.id, label: view.label });
388
- }
389
- }
390
- }
391
- return out;
392
- }
393
- /**
394
- * Test-only reset. Tears down any active shard entries (without running
395
- * deactivate hooks — tests should run deactivate explicitly if they care)
396
- * and clears both registered and active maps.
397
- */
398
- export function __resetShardRegistryForTest() {
399
- active.clear();
400
- activeShards.clear();
401
- registeredShards.clear();
402
- erroredShards.clear();
403
- }