sh3-core 0.22.0 → 0.22.2

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 (80) hide show
  1. package/dist/__test__/fixtures.js +1 -1
  2. package/dist/__test__/reset.js +1 -3
  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 +4 -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 +49 -64
  25. package/dist/apps/lifecycle.test.js +30 -76
  26. package/dist/conflicts/adapter-documents.js +1 -2
  27. package/dist/createShell.js +1 -1
  28. package/dist/documents/handle.d.ts +9 -4
  29. package/dist/documents/handle.js +40 -29
  30. package/dist/documents/handle.test.js +60 -51
  31. package/dist/documents/index.d.ts +1 -1
  32. package/dist/documents/types.d.ts +16 -26
  33. package/dist/host.d.ts +1 -1
  34. package/dist/host.js +9 -56
  35. package/dist/host.svelte.test.js +31 -63
  36. package/dist/layouts-shard/LayoutsSection.svelte +1 -1
  37. package/dist/layouts-shard/layoutsShard.svelte.js +2 -5
  38. package/dist/layouts-shard/layoutsShard.svelte.test.js +2 -2
  39. package/dist/projects-shard/projectsShard.svelte.js +1 -5
  40. package/dist/registry/installer.js +1 -1
  41. package/dist/registry/loader.d.ts +1 -1
  42. package/dist/registry/loader.js +3 -3
  43. package/dist/registry/permission-descriptions.test.js +2 -2
  44. package/dist/registry/register.js +1 -1
  45. package/dist/registry/register.test.js +1 -1
  46. package/dist/runtime/runVerb-shell.test.js +1 -1
  47. package/dist/runtime/runVerb.js +2 -2
  48. package/dist/runtime/runVerb.test.js +9 -9
  49. package/dist/server-shard/types.d.ts +56 -0
  50. package/dist/sh3Api/headless.js +1 -1
  51. package/dist/sh3core-shard/sh3coreShard.svelte.js +1 -6
  52. package/dist/shards/ctx-fetch.test.js +9 -9
  53. package/dist/shards/lifecycle.svelte.d.ts +108 -0
  54. package/dist/shards/lifecycle.svelte.js +551 -0
  55. package/dist/shards/lifecycle.test.js +139 -0
  56. package/dist/shards/types.d.ts +30 -63
  57. package/dist/shell-shard/shellShard.svelte.js +1 -4
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/package.json +1 -1
  61. package/dist/shards/activate-browse.test.js +0 -120
  62. package/dist/shards/activate-contributions.test.js +0 -141
  63. package/dist/shards/activate-error-isolation.test.d.ts +0 -1
  64. package/dist/shards/activate-error-isolation.test.js +0 -98
  65. package/dist/shards/activate-fields.svelte.test.d.ts +0 -1
  66. package/dist/shards/activate-fields.svelte.test.js +0 -121
  67. package/dist/shards/activate-on-key-revoked.test.d.ts +0 -1
  68. package/dist/shards/activate-on-key-revoked.test.js +0 -60
  69. package/dist/shards/activate-runtime.test.d.ts +0 -1
  70. package/dist/shards/activate-runtime.test.js +0 -344
  71. package/dist/shards/activate-scopeid.test.d.ts +0 -1
  72. package/dist/shards/activate-scopeid.test.js +0 -21
  73. package/dist/shards/activate.svelte.d.ts +0 -102
  74. package/dist/shards/activate.svelte.js +0 -407
  75. package/dist/shards/app-binding.svelte.d.ts +0 -8
  76. package/dist/shards/app-binding.svelte.js +0 -30
  77. package/dist/shards/app-binding.test.d.ts +0 -1
  78. package/dist/shards/app-binding.test.js +0 -25
  79. /package/dist/{shards/activate-browse.test.d.ts → actions/dispatcher-v3.test.d.ts} +0 -0
  80. /package/dist/shards/{activate-contributions.test.d.ts → lifecycle.test.d.ts} +0 -0
@@ -25,7 +25,7 @@ export function makeShardManifest(overrides = {}) {
25
25
  }
26
26
  export function makeShard(overrides = {}) {
27
27
  var _a;
28
- return Object.assign({ manifest: makeShardManifest(overrides.manifest), activate: (_a = overrides.activate) !== null && _a !== void 0 ? _a : (() => { }), deactivate: overrides.deactivate }, overrides);
28
+ return Object.assign({ manifest: makeShardManifest(overrides.manifest), register: (_a = overrides.register) !== null && _a !== void 0 ? _a : (() => { }), deactivate: overrides.deactivate }, overrides);
29
29
  }
30
30
  export function makeSlotNode(slotId, viewId) {
31
31
  const id = slotId !== null && slotId !== void 0 ? slotId : uid('slot');
@@ -8,8 +8,7 @@ import { __resetDragStateForTest } from '../layout/drag.svelte';
8
8
  import { __resetLayoutStoreForTest } from '../layout/store.svelte';
9
9
  import { resetSlotHostPool } from '../layout/slotHostPool.svelte';
10
10
  import { __resetViewRegistryForTest } from '../shards/registry';
11
- import { __resetShardRegistryForTest } from '../shards/activate.svelte';
12
- import { __resetShardBindingsForTest } from '../shards/app-binding.svelte';
11
+ import { __resetShardRegistryForTest } from '../shards/lifecycle.svelte';
13
12
  import { __resetAppRegistryForTest } from '../apps/registry.svelte';
14
13
  import { __resetDispatcherStateForTest } from '../actions/state.svelte';
15
14
  import { __resetSelectionForTest } from '../actions/selection.svelte';
@@ -36,7 +35,6 @@ export function resetFramework() {
36
35
  resetSlotHostPool();
37
36
  __resetViewRegistryForTest();
38
37
  __resetShardRegistryForTest();
39
- __resetShardBindingsForTest();
40
38
  __resetAppRegistryForTest();
41
39
  __resetDispatcherStateForTest();
42
40
  __resetSelectionForTest();
@@ -1,11 +1,11 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { resetFramework } from './reset';
3
3
  import { registeredApps, activeApp } from '../apps/registry.svelte';
4
- import { registeredShards, activeShards } from '../shards/activate.svelte';
4
+ import { registeredShards, activeShards } from '../shards/lifecycle.svelte';
5
5
  import { layoutStore } from '../layout/store.svelte';
6
6
  import { makeApp, makeShard } from './fixtures';
7
7
  import { registerApp } from '../apps/registry.svelte';
8
- import { registerShard } from '../shards/activate.svelte';
8
+ import { registerShard } from '../shards/lifecycle.svelte';
9
9
  describe('resetFramework', () => {
10
10
  beforeEach(resetFramework);
11
11
  it('returns all registries and the layout store to boot state', () => {
@@ -4,7 +4,7 @@ const mkEntry = (a, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, a),
6
6
  });
7
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
7
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
8
8
  describe('buildContextMenuModel', () => {
9
9
  it('returns only actions with contextItem: true', () => {
10
10
  const entries = [
@@ -59,7 +59,10 @@ describe('buildContextMenuModel', () => {
59
59
  const model = buildContextMenuModel(entries, state, { element: 'cell' });
60
60
  expect(model.tiers).toEqual([]);
61
61
  });
62
- it('app anchor honors the owner-shard guard', () => {
62
+ it('app anchor admits app-scoped actions from any shard once an app is active (v3)', () => {
63
+ // v3: scope 'app' means "active while any app is launched", regardless
64
+ // of which shard owns the action. The dispatcher no longer guards on
65
+ // ownerShardId / activeAppRequiredShards.
63
66
  const state = mkState({
64
67
  activeAppId: 'app.a',
65
68
  activeAppRequiredShards: new Set(['shard.x']),
@@ -69,7 +72,7 @@ describe('buildContextMenuModel', () => {
69
72
  mkEntry({ id: 'foreign', scope: 'app', contextItem: true, label: 'F' }, 'shard.y'),
70
73
  ];
71
74
  const model = buildContextMenuModel(entries, state, 'app');
72
- expect(model.tiers.flatMap((t) => t.items).map((i) => i.id)).toEqual(['mine']);
75
+ expect(model.tiers.flatMap((t) => t.items).map((i) => i.id).sort()).toEqual(['foreign', 'mine']);
73
76
  });
74
77
  it('home anchor only admits actions when no app is active', () => {
75
78
  const noApp = mkState();
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
3
  import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
- import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/activate.svelte';
4
+ import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/lifecycle.svelte';
5
5
  import { __resetViewRegistryForTest } from '../shards/registry';
6
6
  import { __resetActionsRegistryForTest } from './registry';
7
7
  import { __resetContributionsForTest } from '../contributions/registry';
@@ -19,7 +19,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
19
19
  it('listActions enumerates actions registered by other shards', async () => {
20
20
  registerShard({
21
21
  manifest: { id: 'producer', label: 'P', version: '0.0.0', views: [] },
22
- activate(ctx) {
22
+ register(ctx) {
23
23
  ctx.actions.register({
24
24
  id: 'producer.do',
25
25
  label: 'Do',
@@ -31,7 +31,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
31
31
  let consumerCtx = null;
32
32
  registerShard({
33
33
  manifest: { id: 'consumer', label: 'C', version: '0.0.0', views: [] },
34
- activate(ctx) { consumerCtx = ctx; },
34
+ register(ctx) { consumerCtx = ctx; },
35
35
  });
36
36
  await activateShard('producer');
37
37
  await activateShard('consumer');
@@ -45,7 +45,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
45
45
  it('listActions({ activeOnly: true }) filters out inactive actions', async () => {
46
46
  registerShard({
47
47
  manifest: { id: 'producer', label: 'P', version: '0.0.0', views: [] },
48
- activate(ctx) {
48
+ register(ctx) {
49
49
  ctx.actions.register({
50
50
  id: 'home.go', label: 'H', scope: 'home', run: () => { },
51
51
  });
@@ -58,7 +58,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
58
58
  let consumerCtx = null;
59
59
  registerShard({
60
60
  manifest: { id: 'consumer', label: 'C', version: '0.0.0', views: [] },
61
- activate(ctx) { consumerCtx = ctx; },
61
+ register(ctx) { consumerCtx = ctx; },
62
62
  });
63
63
  await activateShard('producer');
64
64
  await activateShard('consumer');
@@ -71,7 +71,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
71
71
  let invokedVia = null;
72
72
  registerShard({
73
73
  manifest: { id: 'producer', label: 'P', version: '0.0.0', views: [] },
74
- activate(ctx) {
74
+ register(ctx) {
75
75
  ctx.actions.register({
76
76
  id: 'producer.go',
77
77
  label: 'Go',
@@ -83,7 +83,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
83
83
  let consumerCtx = null;
84
84
  registerShard({
85
85
  manifest: { id: 'consumer', label: 'C', version: '0.0.0', views: [] },
86
- activate(ctx) { consumerCtx = ctx; },
86
+ register(ctx) { consumerCtx = ctx; },
87
87
  });
88
88
  await activateShard('producer');
89
89
  await activateShard('consumer');
@@ -93,7 +93,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
93
93
  it('runAction rejects when the target action is inactive', async () => {
94
94
  registerShard({
95
95
  manifest: { id: 'producer', label: 'P', version: '0.0.0', views: [] },
96
- activate(ctx) {
96
+ register(ctx) {
97
97
  ctx.actions.register({
98
98
  id: 'gated.go', label: 'G', scope: 'app', run: () => { },
99
99
  });
@@ -102,7 +102,7 @@ describe('ShardContext.listActions / runAction (integration)', () => {
102
102
  let consumerCtx = null;
103
103
  registerShard({
104
104
  manifest: { id: 'consumer', label: 'C', version: '0.0.0', views: [] },
105
- activate(ctx) { consumerCtx = ctx; },
105
+ register(ctx) { consumerCtx = ctx; },
106
106
  });
107
107
  await activateShard('producer');
108
108
  await activateShard('consumer');
@@ -0,0 +1,8 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getLiveDispatcherState } from './state.svelte';
3
+ describe('dispatcher state — v3', () => {
4
+ it('no longer exposes autostartShards', () => {
5
+ const state = getLiveDispatcherState();
6
+ expect('autostartShards' in state).toBe(false);
7
+ });
8
+ });
@@ -4,7 +4,6 @@ import type { Platform } from './shortcuts';
4
4
  export interface DispatcherState {
5
5
  activeAppId: string | null;
6
6
  activeAppRequiredShards: Set<string>;
7
- autostartShards: Set<string>;
8
7
  mountedViewIds: Set<string>;
9
8
  focusedViewId: string | null;
10
9
  selection: Selection | null;
@@ -20,7 +19,7 @@ export interface TierIndex {
20
19
  app: Map<string, string>;
21
20
  home: Map<string, string>;
22
21
  }
23
- export declare function isScopeActive(scope: AtomicScope, state: DispatcherState, ownerShardId?: string): boolean;
22
+ export declare function isScopeActive(scope: AtomicScope, state: DispatcherState, _ownerShardId?: string): boolean;
24
23
  export declare function buildTierIndex(entries: ActionEntry[], state: DispatcherState): TierIndex;
25
24
  export interface KeydownEnv {
26
25
  target: EventTarget | null;
@@ -8,17 +8,16 @@ import { resolveLabel } from './types';
8
8
  import { effectiveShortcut } from './bindings';
9
9
  import { scopeToTier, normalizeScope } from './scope-helpers';
10
10
  export const TIER_ORDER = ['element', 'focus', 'view', 'app', 'home'];
11
- export function isScopeActive(scope, state, ownerShardId) {
11
+ export function isScopeActive(scope, state, _ownerShardId) {
12
12
  if (scope === 'home') {
13
13
  return state.activeAppId === null;
14
14
  }
15
15
  if (scope === 'app') {
16
- if (state.activeAppId === null)
17
- return false;
18
- if (!ownerShardId)
19
- return false;
20
- return (state.activeAppRequiredShards.has(ownerShardId) ||
21
- state.autostartShards.has(ownerShardId));
16
+ // v3: scope 'app' means "active while any app is launched", regardless
17
+ // of which shard owns the action. The dispatcher no longer gates on
18
+ // shard ownership — every shard registered with register() can
19
+ // contribute app-scoped actions that fire inside any app.
20
+ return state.activeAppId !== null;
22
21
  }
23
22
  if (typeof scope === 'string') {
24
23
  if (scope.startsWith('view:')) {
@@ -4,28 +4,27 @@ const mkEntry = (action, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, action),
6
6
  });
7
- const mkState = (overrides = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, overrides));
7
+ const mkState = (overrides = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, overrides));
8
8
  describe('isScopeActive', () => {
9
9
  it('home active iff no app', () => {
10
10
  expect(isScopeActive('home', mkState())).toBe(true);
11
11
  expect(isScopeActive('home', mkState({ activeAppId: 'app.a' }))).toBe(false);
12
12
  });
13
- it('app active when app is loaded and declaring shard is required', () => {
13
+ it('app active for ANY shard when an app is loaded (v3 owner gate removed)', () => {
14
14
  const state = mkState({
15
15
  activeAppId: 'app.a',
16
16
  activeAppRequiredShards: new Set(['shard.x']),
17
17
  });
18
18
  expect(isScopeActive('app', state, 'shard.x')).toBe(true);
19
- expect(isScopeActive('app', state, 'shard.y')).toBe(false);
20
- });
21
- it('app active for autostart shards regardless of requiredShards', () => {
22
- const state = mkState({
23
- activeAppId: 'app.a',
24
- activeAppRequiredShards: new Set(),
25
- autostartShards: new Set(['__sh3core__']),
26
- });
19
+ // v3: owner shard no longer gates app-scoped actions.
20
+ expect(isScopeActive('app', state, 'shard.y')).toBe(true);
21
+ // v3: no autostart concept same rule applies.
27
22
  expect(isScopeActive('app', state, '__sh3core__')).toBe(true);
28
23
  });
24
+ it('app inactive when no app is loaded', () => {
25
+ const state = mkState({ activeAppId: null });
26
+ expect(isScopeActive('app', state, 'shard.x')).toBe(false);
27
+ });
29
28
  it('view:X active iff X is mounted', () => {
30
29
  const state = mkState({ mountedViewIds: new Set(['editor']) });
31
30
  expect(isScopeActive('view:editor', state)).toBe(true);
@@ -66,7 +65,6 @@ describe('buildTierIndex', () => {
66
65
  ];
67
66
  const state = mkState({
68
67
  activeAppId: null, // home active
69
- autostartShards: new Set(['shard.x']),
70
68
  });
71
69
  const idx = buildTierIndex(entries, state);
72
70
  // Home is active; app is not (no app).
@@ -127,7 +125,6 @@ describe('dispatchKeydown', () => {
127
125
  const state = mkState({
128
126
  activeAppId: 'a',
129
127
  activeAppRequiredShards: new Set(),
130
- autostartShards: new Set(['__sh3core__']),
131
128
  selection: { type: 'orb', ref: 1, ownerShardId: 's' },
132
129
  });
133
130
  const result = dispatchKeydown(mkEnv({ entries, runAction, state }));
@@ -4,7 +4,7 @@ const mkEntry = (a, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, a),
6
6
  });
7
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
7
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
8
8
  describe('listActionsFromEntries', () => {
9
9
  it('returns [] for an empty registry', () => {
10
10
  expect(listActionsFromEntries([], mkState())).toEqual([]);
@@ -55,7 +55,6 @@ describe('listActionsFromEntries', () => {
55
55
  ];
56
56
  const state = mkState({
57
57
  activeAppId: null, // app tier inactive
58
- autostartShards: new Set(['__sh3core__']),
59
58
  mountedViewIds: new Set(['editor']),
60
59
  });
61
60
  const out = listActionsFromEntries(entries, state);
@@ -4,7 +4,7 @@ const mkEntry = (a, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, a),
6
6
  });
7
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
7
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
8
8
  describe('listActiveFromEntries', () => {
9
9
  it('omits actions whose scope is not active', () => {
10
10
  const entries = [mkEntry({ id: 'p', scope: 'app' })];
@@ -27,7 +27,7 @@ describe('listActiveFromEntries', () => {
27
27
  id: 'm', scope: ['app', 'view:editor'],
28
28
  }, '__sh3core__')];
29
29
  const state = mkState({
30
- activeAppId: 'a', autostartShards: new Set(['__sh3core__']),
30
+ activeAppId: 'a',
31
31
  mountedViewIds: new Set(['editor']),
32
32
  });
33
33
  const out = listActiveFromEntries(entries, state);
@@ -76,7 +76,6 @@ describe('listActiveFromEntries', () => {
76
76
  ];
77
77
  const state = mkState({
78
78
  activeAppId: 'a',
79
- autostartShards: new Set(['__sh3core__']),
80
79
  mountedViewIds: new Set(['editor']),
81
80
  });
82
81
  const out = listActiveFromEntries(entries, state);
@@ -4,7 +4,7 @@ const mkEntry = (a, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, a),
6
6
  });
7
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
7
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
8
8
  describe('resolveMenuContainers', () => {
9
9
  it('returns [] when no app is active', () => {
10
10
  expect(resolveMenuContainers(null, undefined)).toEqual([]);
@@ -72,7 +72,6 @@ describe('resolveMenuItems', () => {
72
72
  const state = mkState({
73
73
  activeAppId: 'app.a',
74
74
  activeAppRequiredShards: new Set(['shard.x']),
75
- autostartShards: new Set(['shard.x']),
76
75
  });
77
76
  const entries = [
78
77
  mkEntry({ id: 'p', scope: ['home', 'app'], menuItem: 'file', label: 'P' }),
@@ -161,7 +160,6 @@ describe("resolveMenuItems — required-shard menu filter (issue #32)", () => {
161
160
  const state = mkState({
162
161
  activeAppId: 'other-app',
163
162
  activeAppRequiredShards: new Set(['other-shard']),
164
- autostartShards: new Set(['guml.core']),
165
163
  });
166
164
  const entries = [
167
165
  mkEntry({ id: 'guml.project.new', scope: 'app', menuItem: 'file', label: 'New Project…' }, 'guml.core'),
@@ -172,7 +170,6 @@ describe("resolveMenuItems — required-shard menu filter (issue #32)", () => {
172
170
  const state = mkState({
173
171
  activeAppId: 'guml-ide',
174
172
  activeAppRequiredShards: new Set(['guml.core']),
175
- autostartShards: new Set(['guml.core']),
176
173
  });
177
174
  const entries = [
178
175
  mkEntry({ id: 'guml.project.new', scope: 'app', menuItem: 'file', label: 'New Project…' }, 'guml.core'),
@@ -184,7 +181,6 @@ describe("resolveMenuItems — required-shard menu filter (issue #32)", () => {
184
181
  const state = mkState({
185
182
  activeAppId: 'other-app',
186
183
  activeAppRequiredShards: new Set(['other-shard']),
187
- autostartShards: new Set(['guml.core']),
188
184
  mountedViewIds: new Set(['editor']),
189
185
  });
190
186
  const entries = [
@@ -196,7 +192,6 @@ describe("resolveMenuItems — required-shard menu filter (issue #32)", () => {
196
192
  const state = mkState({
197
193
  activeAppId: 'other-app',
198
194
  activeAppRequiredShards: new Set(['other-shard']),
199
- autostartShards: new Set(['guml.core']),
200
195
  });
201
196
  const entries = [
202
197
  mkEntry({ id: 'g.global', scope: ['home', 'app'], menuItem: 'file', label: 'Global' }, 'guml.core'),
@@ -207,7 +202,6 @@ describe("resolveMenuItems — required-shard menu filter (issue #32)", () => {
207
202
  const state = mkState({
208
203
  activeAppId: 'other-app',
209
204
  activeAppRequiredShards: new Set(['other-shard']),
210
- autostartShards: new Set(['guml.core']),
211
205
  });
212
206
  const entries = [
213
207
  mkEntry({ id: 'g.parent', scope: 'app', menuItem: 'file', label: 'GUML', submenu: true }, 'guml.core'),
@@ -4,7 +4,7 @@ const mkEntry = (a, owner = 'shard.x') => ({
4
4
  ownerShardId: owner,
5
5
  action: Object.assign({ id: 'a', label: 'A', scope: 'home', run: () => { } }, a),
6
6
  });
7
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
7
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
8
8
  describe('buildPaletteCandidates', () => {
9
9
  it('includes actions with paletteItem default true', () => {
10
10
  const entries = [mkEntry({ id: 'x', scope: 'home', label: 'X' })];
@@ -25,7 +25,6 @@ describe('buildPaletteCandidates', () => {
25
25
  const entries = [mkEntry({ id: 'p', scope: ['home', 'app'], label: 'P' }, '__sh3core__')];
26
26
  const state = mkState({
27
27
  activeAppId: 'a',
28
- autostartShards: new Set(['__sh3core__']),
29
28
  });
30
29
  const out = buildPaletteCandidates(entries, state);
31
30
  expect(out).toHaveLength(1);
@@ -39,7 +38,6 @@ describe('buildPaletteCandidates', () => {
39
38
  ];
40
39
  const state = mkState({
41
40
  activeAppId: 'a',
42
- autostartShards: new Set(['__sh3core__']),
43
41
  mountedViewIds: new Set(['editor']),
44
42
  });
45
43
  const out = buildPaletteCandidates(entries, state);
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import { scopeToTier, normalizeScope, scopeBadge, innermostActiveScope, scopeEquals, scopeToString, parseScopeString, } from './scope-helpers';
3
- const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), autostartShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
3
+ const mkState = (o = {}) => (Object.assign({ activeAppId: null, activeAppRequiredShards: new Set(), mountedViewIds: new Set(), focusedViewId: null, selection: null, bindings: {}, platform: 'other' }, o));
4
4
  describe('scopeToTier', () => {
5
5
  it('maps atoms to tier names', () => {
6
6
  expect(scopeToTier('home')).toBe('home');
@@ -37,7 +37,7 @@ describe('innermostActiveScope', () => {
37
37
  });
38
38
  it('picks the innermost active tier across a multi-scope action', () => {
39
39
  const state = mkState({
40
- activeAppId: 'a', autostartShards: new Set(['owner']),
40
+ activeAppId: 'a',
41
41
  mountedViewIds: new Set(['editor']),
42
42
  });
43
43
  const winner = innermostActiveScope(['app', 'view:editor'], state, 'owner');
@@ -45,14 +45,14 @@ describe('innermostActiveScope', () => {
45
45
  });
46
46
  it('falls back to outer tier when inner is inactive', () => {
47
47
  const state = mkState({
48
- activeAppId: 'a', autostartShards: new Set(['owner']),
48
+ activeAppId: 'a',
49
49
  });
50
50
  const winner = innermostActiveScope(['app', 'view:editor'], state, 'owner');
51
51
  expect(winner).toBe('app');
52
52
  });
53
53
  it('element scope beats view scope when both active', () => {
54
54
  const state = mkState({
55
- activeAppId: 'a', autostartShards: new Set(['owner']),
55
+ activeAppId: 'a',
56
56
  mountedViewIds: new Set(['editor']),
57
57
  selection: { type: 'row', ref: {}, ownerShardId: 'owner' },
58
58
  });
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
- import { registerShard, activateShard, deactivateShard } from '../shards/activate.svelte';
2
+ import { registerShard, activateShard, deactivateShard } from '../shards/lifecycle.svelte';
3
3
  import { __resetContributionsForTest } from '../contributions/registry';
4
4
  import { __resetActionsRegistryForTest, listActions } from './registry';
5
5
  import { __resetSelectionForTest, getSelection } from './selection.svelte';
6
6
  function mkShard(id, onActivate) {
7
7
  return {
8
8
  manifest: { id, label: id, version: '0.0.0-test', views: [] },
9
- async activate(ctx) { onActivate(ctx); },
9
+ async register(ctx) { onActivate(ctx); },
10
10
  };
11
11
  }
12
12
  describe('ctx.actions on ShardContext', () => {
@@ -12,7 +12,6 @@ export declare function __notifyActiveChange(): void;
12
12
  /** Test-only alias for the internal notifier. */
13
13
  export declare const __notifyActiveChangeForTest: typeof __notifyActiveChange;
14
14
  export declare function setActiveApp(appId: string | null, requiredShards: Set<string>): void;
15
- export declare function setAutostartShards(shards: Set<string>): void;
16
15
  export declare function setMountedViewIds(ids: Set<string>): void;
17
16
  /**
18
17
  * One-shot snapshot: walk the active layout tree and update
@@ -24,5 +23,16 @@ export declare function syncMountedViewIdsFromLayout(): void;
24
23
  export declare function setFocusedViewId(id: string | null): void;
25
24
  export declare function setUserBindings(bindings: Record<string, string | null>): void;
26
25
  export declare function getLiveDispatcherState(): DispatcherState;
27
- export declare function addAutostartShard(id: string): void;
26
+ /**
27
+ * @deprecated v3: autostartShards has been removed. This is a no-op shim
28
+ * retained so existing test fixtures compile during the migration window.
29
+ * Phase 7 sweeps these call sites.
30
+ */
31
+ export declare function addAutostartShard(_id: string): void;
32
+ /**
33
+ * @deprecated v3: autostartShards has been removed. This is a no-op shim
34
+ * retained so existing test fixtures compile during the migration window.
35
+ * Phase 7 sweeps these call sites.
36
+ */
37
+ export declare function setAutostartShards(_shards: Set<string>): void;
28
38
  export declare function __resetDispatcherStateForTest(): void;
@@ -13,7 +13,6 @@ import { layoutStore } from '../layout/store.svelte';
13
13
  import { collectTreeSlotRefs } from '../layout/tree-walk';
14
14
  let activeAppId = $state(null);
15
15
  let activeAppRequiredShards = $state(new Set());
16
- let autostartShards = $state(new Set());
17
16
  let mountedViewIds = $state(new Set());
18
17
  let focusedViewId = $state(null);
19
18
  let userBindings = $state({});
@@ -51,10 +50,6 @@ export function setActiveApp(appId, requiredShards) {
51
50
  activeAppRequiredShards = new Set(requiredShards);
52
51
  notifyActiveChange();
53
52
  }
54
- export function setAutostartShards(shards) {
55
- autostartShards = new Set(shards);
56
- notifyActiveChange();
57
- }
58
53
  export function setMountedViewIds(ids) {
59
54
  mountedViewIds = new Set(ids);
60
55
  notifyActiveChange();
@@ -87,7 +82,6 @@ export function getLiveDispatcherState() {
87
82
  return {
88
83
  activeAppId,
89
84
  activeAppRequiredShards,
90
- autostartShards,
91
85
  mountedViewIds,
92
86
  focusedViewId,
93
87
  selection: getSelection(),
@@ -95,16 +89,25 @@ export function getLiveDispatcherState() {
95
89
  platform,
96
90
  };
97
91
  }
98
- export function addAutostartShard(id) {
99
- if (!autostartShards.has(id)) {
100
- autostartShards = new Set([...autostartShards, id]);
101
- notifyActiveChange();
102
- }
92
+ /**
93
+ * @deprecated v3: autostartShards has been removed. This is a no-op shim
94
+ * retained so existing test fixtures compile during the migration window.
95
+ * Phase 7 sweeps these call sites.
96
+ */
97
+ export function addAutostartShard(_id) {
98
+ /* no-op */
99
+ }
100
+ /**
101
+ * @deprecated v3: autostartShards has been removed. This is a no-op shim
102
+ * retained so existing test fixtures compile during the migration window.
103
+ * Phase 7 sweeps these call sites.
104
+ */
105
+ export function setAutostartShards(_shards) {
106
+ /* no-op */
103
107
  }
104
108
  export function __resetDispatcherStateForTest() {
105
109
  activeAppId = null;
106
110
  activeAppRequiredShards = new Set();
107
- autostartShards = new Set();
108
111
  mountedViewIds = new Set();
109
112
  focusedViewId = null;
110
113
  userBindings = {};
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
- import { setActiveApp, setAutostartShards, setFocusedViewId, setMountedViewIds, setUserBindings, getLiveDispatcherState, onActiveChange, __notifyActiveChangeForTest, __resetDispatcherStateForTest, } from './state.svelte';
2
+ import { setActiveApp, setFocusedViewId, setMountedViewIds, setUserBindings, getLiveDispatcherState, onActiveChange, __notifyActiveChangeForTest, __resetDispatcherStateForTest, } from './state.svelte';
3
3
  import { __resetSelectionForTest, makeSelectionApi } from './selection.svelte';
4
4
  describe('live dispatcher state', () => {
5
5
  beforeEach(() => {
@@ -46,15 +46,15 @@ describe('onActiveChange', () => {
46
46
  it('fires on every setter', () => {
47
47
  let n = 0;
48
48
  const off = onActiveChange(() => { n++; });
49
+ // v3: setAutostartShards is a no-op shim; 4 setters notify.
49
50
  setActiveApp('a', new Set(['s']));
50
- setAutostartShards(new Set(['s']));
51
51
  setMountedViewIds(new Set(['v']));
52
52
  setFocusedViewId('v');
53
53
  setUserBindings({ foo: 'Ctrl+K' });
54
- expect(n).toBe(5);
54
+ expect(n).toBe(4);
55
55
  off();
56
56
  setActiveApp(null, new Set());
57
- expect(n).toBe(5);
57
+ expect(n).toBe(4);
58
58
  });
59
59
  it('fires on external notify (used for registry change)', () => {
60
60
  let n = 0;
package/dist/api.d.ts CHANGED
@@ -27,7 +27,7 @@ export { spawnSatellite } from './sh3Api/window';
27
27
  export type { SpawnSatelliteOptions } from './sh3Api/window';
28
28
  export type { SatellitePayload } from './boot/satellitePayload';
29
29
  export { inspectActiveLayout, spliceIntoActiveLayout, dockIntoActiveLayout, focusTab, focusView, collapseChild, expandChild, closeTab, popoutView, dockFloat, locateSlot, } from './layout/inspection';
30
- export type { DocumentHandle, DocumentHandleOptions, DocumentFormat, DocumentMeta, DocumentChange, AutosaveController, } from './documents/types';
30
+ export type { DocumentHandle, DocumentMeta, DocumentChange, AutosaveController, } from './documents/types';
31
31
  export { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from './documents/types';
32
32
  export type { BrowseCapability } from './documents/browse';
33
33
  export type { DocumentPickerApi, DocumentOpenOptions, DocumentSaveOptions } from './documents/picker-api';
@@ -38,8 +38,8 @@ export type { ConflictItem, ConflictBranch as ConflictManagerBranch, ResolveOpti
38
38
  export { CONFLICT_RENDERER_POINT, ConflictPermissionError, ConflictSessionOrphanedError, } from './conflicts/api';
39
39
  export type { ColorPickOptions, ColorContribution, ColorApi, } from './color/api';
40
40
  export { COLOR_PICKER_POINT } from './color/api';
41
- export { registeredShards, activeShards, erroredShards } from './shards/activate.svelte';
42
- export type { ShardErrorEntry } from './shards/activate.svelte';
41
+ export { registeredShards, activeShards, erroredShards } from './shards/lifecycle.svelte';
42
+ export type { ShardErrorEntry } from './shards/lifecycle.svelte';
43
43
  export type { RegistryIndex, PackageEntry, PackageVersion, RequiredDependency, InstalledPackage, InstallResult, PackageMeta, RemoteInstallRequest, } from './registry/types';
44
44
  export type { ResolvedPackage } from './registry/client';
45
45
  export { fetchRegistries, fetchArchive, buildPackageMeta } from './registry/client';
@@ -55,6 +55,7 @@ export declare const capabilities: {
55
55
  readonly hotInstall: boolean;
56
56
  };
57
57
  export type { ServerShard, ServerShardContext, TenantDocumentAPI } from './server-shard/types';
58
+ export type { ApiKeyPublic as ServerApiKeyPublic } from './server-shard/types';
58
59
  export type { Verb, VerbContext, Sh3Api, VerbSchema, PortableJSONSchema, DispatchToTerminalResult, } from './verbs/types';
59
60
  export type { Scrollback } from './shell-shard/scrollback.svelte';
60
61
  export type { SessionClient } from './shell-shard/session-client.svelte';
package/dist/api.js CHANGED
@@ -42,7 +42,7 @@ export { COLOR_PICKER_POINT } from './color/api';
42
42
  // and tooling shards that need to visualize framework state. Phase 9
43
43
  // addition: diagnostic used to reach `activate.svelte` directly via $lib;
44
44
  // the package boundary requires routing through the public surface.
45
- export { registeredShards, activeShards, erroredShards } from './shards/activate.svelte';
45
+ export { registeredShards, activeShards, erroredShards } from './shards/lifecycle.svelte';
46
46
  export { fetchRegistries, fetchArchive, buildPackageMeta } from './registry/client';
47
47
  export { validateRegistryIndex } from './registry/schema';
48
48
  // Key mint/revoke types — client shards that declare `keys:mint` get ctx.keys.
@@ -33,7 +33,7 @@ export const adminShard = {
33
33
  { id: 'sh3-admin:mounts', label: 'Mounts' },
34
34
  ],
35
35
  },
36
- activate(ctx) {
36
+ register(ctx) {
37
37
  const usersFactory = {
38
38
  mount(container, _context) {
39
39
  const instance = mount(UsersView, { target: container });
@@ -69,7 +69,7 @@ export const storeShard = {
69
69
  { id: 'sh3-store:browse', label: 'Store' },
70
70
  ],
71
71
  },
72
- activate(ctx) {
72
+ register(ctx) {
73
73
  const env = ctx.env({ registries: [] });
74
74
  const state = ctx.state({
75
75
  ephemeral: {
@@ -260,9 +260,14 @@ export const storeShard = {
260
260
  ctx.registerVerb(updateVerb);
261
261
  // refreshInstalled can run immediately (hits server, no env needed).
262
262
  refreshInstalled();
263
- },
264
- autostart() {
265
- // Runs after env hydration, so registries are populated.
266
- storeContext.refreshCatalog();
263
+ // v3: env hydration happens after register() returns. React via $effect
264
+ // so refreshCatalog fires once registries populate.
265
+ $effect.root(() => {
266
+ $effect(() => {
267
+ if (env.registries.length > 0) {
268
+ storeContext.refreshCatalog();
269
+ }
270
+ });
271
+ });
267
272
  },
268
273
  };