sh3-core 0.11.7 → 0.12.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.
Files changed (36) hide show
  1. package/dist/actions/contextMenuModel.d.ts +5 -4
  2. package/dist/actions/contextMenuModel.js +26 -12
  3. package/dist/actions/contextMenuModel.test.js +49 -24
  4. package/dist/actions/listeners.d.ts +2 -0
  5. package/dist/actions/listeners.js +65 -6
  6. package/dist/actions/listeners.test.js +96 -8
  7. package/dist/actions/scope-helpers.d.ts +23 -0
  8. package/dist/actions/scope-helpers.js +47 -0
  9. package/dist/actions/scope-helpers.test.js +56 -1
  10. package/dist/actions/types.d.ts +1 -0
  11. package/dist/api.d.ts +2 -1
  12. package/dist/api.js +1 -1
  13. package/dist/app/store/InstalledView.svelte +2 -1
  14. package/dist/app/store/StoreView.svelte +2 -1
  15. package/dist/apps/lifecycle.d.ts +7 -0
  16. package/dist/apps/lifecycle.js +22 -5
  17. package/dist/apps/lifecycle.test.js +50 -0
  18. package/dist/documents/browse.d.ts +15 -0
  19. package/dist/documents/browse.js +7 -0
  20. package/dist/documents/browse.test.js +41 -0
  21. package/dist/documents/handle.js +3 -1
  22. package/dist/documents/handle.test.js +23 -0
  23. package/dist/host.js +18 -4
  24. package/dist/layout/LayoutRenderer.svelte +5 -1
  25. package/dist/layout/LayoutRenderer.test.js +42 -0
  26. package/dist/layout/SlotContainer.svelte +11 -2
  27. package/dist/layout/SlotContainer.svelte.d.ts +1 -0
  28. package/dist/layout/slotHostPool.svelte.js +10 -3
  29. package/dist/layout/slotHostPool.test.js +15 -0
  30. package/dist/shards/activate-error-isolation.test.d.ts +1 -0
  31. package/dist/shards/activate-error-isolation.test.js +98 -0
  32. package/dist/shards/activate.svelte.d.ts +30 -2
  33. package/dist/shards/activate.svelte.js +62 -17
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +1 -1
@@ -47,6 +47,7 @@ export const registeredShards = $state(new Map());
47
47
  */
48
48
  const active = new Map();
49
49
  export const activeShards = $state(new Map());
50
+ export const erroredShards = $state(new Map());
50
51
  /**
51
52
  * Register (or re-register) a shard with the framework so it can later be
52
53
  * activated. Records the shard in `registeredShards` but does not run
@@ -63,6 +64,9 @@ export function registerShard(shard) {
63
64
  deactivateShard(id);
64
65
  }
65
66
  registeredShards.set(id, shard);
67
+ // Re-registering wipes any prior error: the new shard module gets a
68
+ // clean slate, and a hot-reload of a fixed shard removes the stale entry.
69
+ erroredShards.delete(id);
66
70
  }
67
71
  /**
68
72
  * Activate a registered shard. Builds a `ShardContext`, calls `shard.activate`,
@@ -70,11 +74,18 @@ export function registerShard(shard) {
70
74
  * calls `shard.autostart` if defined. Idempotent — calling on an already-active
71
75
  * shard is a no-op.
72
76
  *
77
+ * If `shard.activate` throws, partial state (registered views, verbs,
78
+ * contributions, document handles, actions, env subscription) is unwound
79
+ * and the failure is recorded in `erroredShards` before the error is
80
+ * re-thrown. Callers in `host.ts` (autostart loop) and `launchApp`
81
+ * (required-shard loop) decide how to react.
82
+ *
73
83
  * @param id - The `ShardManifest.id` of the shard to activate. Must be registered.
74
- * @throws If the shard is not registered, or if a manifest view has no factory after activation.
84
+ * @param opts - Optional. `phase` is recorded in `erroredShards` on failure (default 'launch').
85
+ * @throws If the shard is not registered, if `shard.activate` throws, or if a manifest view has no factory after activation.
75
86
  */
76
- export async function activateShard(id) {
77
- var _a, _b, _c, _d, _e;
87
+ export async function activateShard(id, opts) {
88
+ var _a, _b, _c, _d, _e, _f;
78
89
  const shard = registeredShards.get(id);
79
90
  if (!shard) {
80
91
  throw new Error(`Cannot activate shard "${id}": not registered`);
@@ -201,24 +212,57 @@ export async function activateShard(id) {
201
212
  }
202
213
  active.set(id, entry);
203
214
  activeShards.set(id, shard);
204
- await shard.activate(ctx);
205
- for (const view of shard.manifest.views) {
206
- if (!entry.viewIds.has(view.id)) {
207
- throw new Error(`Shard "${id}" declared view "${view.id}" in its manifest but registered no factory for it.`);
215
+ try {
216
+ await shard.activate(ctx);
217
+ for (const view of shard.manifest.views) {
218
+ if (!entry.viewIds.has(view.id)) {
219
+ throw new Error(`Shard "${id}" declared view "${view.id}" in its manifest but registered no factory for it.`);
220
+ }
208
221
  }
209
- }
210
- // Hydrate env state if the shard declared it via ctx.env().
211
- if (envState.proxy && envState.defaults) {
212
- try {
213
- const stored = await fetchEnvState(id);
214
- const merged = Object.assign({}, envState.defaults, stored);
215
- Object.assign(envState.proxy, merged);
222
+ // Hydrate env state if the shard declared it via ctx.env().
223
+ if (envState.proxy && envState.defaults) {
224
+ try {
225
+ const stored = await fetchEnvState(id);
226
+ const merged = Object.assign({}, envState.defaults, stored);
227
+ Object.assign(envState.proxy, merged);
228
+ }
229
+ catch (err) {
230
+ console.warn(`[sh3] Failed to hydrate env state for shard "${id}":`, err instanceof Error ? err.message : err);
231
+ }
216
232
  }
217
- catch (err) {
218
- console.warn(`[sh3] Failed to hydrate env state for shard "${id}":`, err instanceof Error ? err.message : err);
233
+ }
234
+ catch (err) {
235
+ // Unwind partial state. Mirror deactivateShard's body, minus the
236
+ // shard.deactivate?.() call — the shard never finished activating.
237
+ // Each cleanup fn runs inside its own swallow so a teardown failure
238
+ // cannot mask the original activation error.
239
+ for (const fn of entry.cleanupFns) {
240
+ try {
241
+ void fn();
242
+ }
243
+ catch (_g) {
244
+ // intentionally swallowed: original error is what matters.
245
+ }
219
246
  }
247
+ for (const name of entry.verbNames)
248
+ fwUnregisterVerb(name);
249
+ for (const viewId of entry.viewIds)
250
+ unregisterView(viewId);
251
+ clearSelectionForShard(id);
252
+ active.delete(id);
253
+ activeShards.delete(id);
254
+ erroredShards.set(id, {
255
+ id,
256
+ error: err,
257
+ phase: (_e = opts === null || opts === void 0 ? void 0 : opts.phase) !== null && _e !== void 0 ? _e : 'launch',
258
+ timestamp: Date.now(),
259
+ });
260
+ console.error(`[sh3] Shard "${id}" failed to activate:`, err);
261
+ throw err;
220
262
  }
221
- void ((_e = shard.autostart) === null || _e === void 0 ? void 0 : _e.call(shard, ctx));
263
+ // Activation succeeded clear any prior error record for this shard.
264
+ erroredShards.delete(id);
265
+ void ((_f = shard.autostart) === null || _f === void 0 ? void 0 : _f.call(shard, ctx));
222
266
  }
223
267
  /**
224
268
  * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
@@ -288,4 +332,5 @@ export function __resetShardRegistryForTest() {
288
332
  active.clear();
289
333
  activeShards.clear();
290
334
  registeredShards.clear();
335
+ erroredShards.clear();
291
336
  }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export declare const VERSION = "0.11.7";
2
+ export declare const VERSION = "0.12.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export const VERSION = '0.11.7';
2
+ export const VERSION = '0.12.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.11.7",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"