sh3-core 0.7.3 → 0.8.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.
- package/dist/__test__/fixtures.d.ts +12 -0
- package/dist/__test__/fixtures.js +62 -0
- package/dist/__test__/render.d.ts +3 -0
- package/dist/__test__/render.js +11 -0
- package/dist/__test__/reset.d.ts +14 -0
- package/dist/__test__/reset.js +34 -0
- package/dist/__test__/setup-dom.d.ts +1 -0
- package/dist/__test__/setup-dom.js +26 -0
- package/dist/__test__/smoke.test.d.ts +1 -0
- package/dist/__test__/smoke.test.js +28 -0
- package/dist/api.d.ts +15 -2
- package/dist/api.js +13 -1
- package/dist/app/store/StoreView.svelte +36 -7
- package/dist/app/store/storeShard.svelte.js +9 -3
- package/dist/app/store/verbs.js +8 -2
- package/dist/apps/lifecycle.d.ts +11 -0
- package/dist/apps/lifecycle.js +48 -11
- package/dist/apps/lifecycle.test.d.ts +1 -0
- package/dist/apps/lifecycle.test.js +309 -0
- package/dist/apps/registry.svelte.d.ts +2 -0
- package/dist/apps/registry.svelte.js +5 -0
- package/dist/apps/types.d.ts +24 -2
- package/dist/createShell.d.ts +2 -0
- package/dist/createShell.js +9 -7
- package/dist/documents/handle.js +5 -0
- package/dist/documents/index.d.ts +1 -0
- package/dist/documents/index.js +1 -0
- package/dist/documents/journal-hook.d.ts +6 -0
- package/dist/documents/journal-hook.js +16 -0
- package/dist/documents/sync/activate-integration.test.d.ts +1 -0
- package/dist/documents/sync/activate-integration.test.js +37 -0
- package/dist/documents/sync/components/DocumentSyncExplorer.svelte +99 -0
- package/dist/documents/sync/components/DocumentSyncExplorer.svelte.d.ts +15 -0
- package/dist/documents/sync/components/SyncGrantPicker.svelte +70 -0
- package/dist/documents/sync/components/SyncGrantPicker.svelte.d.ts +12 -0
- package/dist/documents/sync/conflicts.d.ts +30 -0
- package/dist/documents/sync/conflicts.js +77 -0
- package/dist/documents/sync/conflicts.test.d.ts +1 -0
- package/dist/documents/sync/conflicts.test.js +71 -0
- package/dist/documents/sync/engine.d.ts +19 -0
- package/dist/documents/sync/engine.js +188 -0
- package/dist/documents/sync/engine.test.d.ts +1 -0
- package/dist/documents/sync/engine.test.js +169 -0
- package/dist/documents/sync/handle.d.ts +11 -0
- package/dist/documents/sync/handle.js +79 -0
- package/dist/documents/sync/handle.test.d.ts +1 -0
- package/dist/documents/sync/handle.test.js +56 -0
- package/dist/documents/sync/hash.d.ts +1 -0
- package/dist/documents/sync/hash.js +13 -0
- package/dist/documents/sync/hash.test.d.ts +1 -0
- package/dist/documents/sync/hash.test.js +20 -0
- package/dist/documents/sync/index.d.ts +6 -0
- package/dist/documents/sync/index.js +12 -0
- package/dist/documents/sync/journal.d.ts +30 -0
- package/dist/documents/sync/journal.js +179 -0
- package/dist/documents/sync/journal.test.d.ts +1 -0
- package/dist/documents/sync/journal.test.js +87 -0
- package/dist/documents/sync/registry.d.ts +10 -0
- package/dist/documents/sync/registry.js +66 -0
- package/dist/documents/sync/registry.test.d.ts +1 -0
- package/dist/documents/sync/registry.test.js +42 -0
- package/dist/documents/sync/serialization.d.ts +5 -0
- package/dist/documents/sync/serialization.js +24 -0
- package/dist/documents/sync/serialization.test.d.ts +1 -0
- package/dist/documents/sync/serialization.test.js +26 -0
- package/dist/documents/sync/singleton.d.ts +11 -0
- package/dist/documents/sync/singleton.js +26 -0
- package/dist/documents/sync/tombstones.d.ts +19 -0
- package/dist/documents/sync/tombstones.js +58 -0
- package/dist/documents/sync/tombstones.test.d.ts +1 -0
- package/dist/documents/sync/tombstones.test.js +37 -0
- package/dist/documents/sync/types.d.ts +116 -0
- package/dist/documents/sync/types.js +27 -0
- package/dist/documents/sync/write-hook.test.d.ts +1 -0
- package/dist/documents/sync/write-hook.test.js +36 -0
- package/dist/env/client.d.ts +10 -5
- package/dist/env/client.js +12 -4
- package/dist/layout/LayoutRenderer.browser.test.d.ts +1 -0
- package/dist/layout/LayoutRenderer.browser.test.js +274 -0
- package/dist/layout/LayoutRenderer.svelte +2 -1
- package/dist/layout/LayoutRenderer.test.d.ts +1 -0
- package/dist/layout/LayoutRenderer.test.js +143 -0
- package/dist/layout/SlotContainer.svelte +8 -2
- package/dist/layout/SlotDropZone.svelte +19 -0
- package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-1-drag-tab-between-groups-moves-a-tab-from-one-tabs-group-to-another-1.png +0 -0
- package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-2-drag-tab-to-quadrant-creates-a-split-when-dropping-a-tab-on-a-quadrant-drop-zone-1.png +0 -0
- package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-3-splitter-drag-updates-split-sizes-when-the-splitter-handle-is-dragged-1.png +0 -0
- package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-4-close-policy-removes-closable-tabs--keeps-non-closable--and-awaits-canClose-1.png +0 -0
- package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-5-splitter-collapse-toggle-toggles-collapsed-i--on-double-click-1.png +0 -0
- package/dist/layout/drag.svelte.d.ts +5 -0
- package/dist/layout/drag.svelte.js +15 -0
- package/dist/layout/slotHostPool.svelte.d.ts +16 -1
- package/dist/layout/slotHostPool.svelte.js +123 -5
- package/dist/layout/slotHostPool.test.d.ts +1 -0
- package/dist/layout/slotHostPool.test.js +104 -0
- package/dist/layout/store.svelte.d.ts +22 -0
- package/dist/layout/store.svelte.js +78 -16
- package/dist/layout/tree-walk.d.ts +2 -0
- package/dist/layout/tree-walk.js +1 -1
- package/dist/layout/types.d.ts +5 -0
- package/dist/overlays/float.d.ts +2 -0
- package/dist/overlays/float.js +4 -1
- package/dist/overlays/float.test.js +102 -1
- package/dist/primitives/ResizableSplitter.svelte +2 -0
- package/dist/primitives/TabbedPanel.svelte +4 -0
- package/dist/primitives/TabbedPanel.svelte.d.ts +2 -0
- package/dist/registry/installer.d.ts +10 -7
- package/dist/registry/installer.js +39 -35
- package/dist/registry/register.d.ts +17 -0
- package/dist/registry/register.js +22 -0
- package/dist/registry/register.test.d.ts +1 -0
- package/dist/registry/register.test.js +28 -0
- package/dist/shards/activate.svelte.d.ts +6 -0
- package/dist/shards/activate.svelte.js +33 -2
- package/dist/shards/registry.d.ts +4 -0
- package/dist/shards/registry.js +18 -0
- package/dist/shards/types.d.ts +16 -1
- package/dist/shell-shard/Terminal.svelte +140 -33
- package/dist/shell-shard/Terminal.svelte.d.ts +3 -0
- package/dist/shell-shard/auto-relocate.d.ts +12 -0
- package/dist/shell-shard/auto-relocate.js +20 -0
- package/dist/shell-shard/auto-relocate.test.d.ts +1 -0
- package/dist/shell-shard/auto-relocate.test.js +35 -0
- package/dist/shell-shard/dispatch.d.ts +15 -0
- package/dist/shell-shard/dispatch.js +56 -0
- package/dist/shell-shard/modes/builtin.d.ts +5 -0
- package/dist/shell-shard/modes/builtin.js +18 -0
- package/dist/shell-shard/modes/prefs.d.ts +5 -0
- package/dist/shell-shard/modes/prefs.js +31 -0
- package/dist/shell-shard/modes/prefs.test.d.ts +1 -0
- package/dist/shell-shard/modes/prefs.test.js +46 -0
- package/dist/shell-shard/modes/registry.d.ts +7 -0
- package/dist/shell-shard/modes/registry.js +27 -0
- package/dist/shell-shard/modes/registry.test.d.ts +1 -0
- package/dist/shell-shard/modes/registry.test.js +35 -0
- package/dist/shell-shard/modes/types.d.ts +8 -0
- package/dist/shell-shard/modes/types.js +1 -0
- package/dist/shell-shard/protocol.d.ts +6 -0
- package/dist/shell-shard/shellShard.svelte.js +5 -1
- package/dist/shell-shard/tenant-fs-client.d.ts +24 -0
- package/dist/shell-shard/tenant-fs-client.js +44 -0
- package/dist/shell-shard/tenant-fs-client.test.d.ts +1 -0
- package/dist/shell-shard/tenant-fs-client.test.js +49 -0
- package/dist/shell-shard/terminal-dispatch.test.d.ts +1 -0
- package/dist/shell-shard/terminal-dispatch.test.js +53 -0
- package/dist/shell-shard/toolbar/Toolbar.svelte +62 -0
- package/dist/shell-shard/toolbar/Toolbar.svelte.d.ts +11 -0
- package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte +28 -0
- package/dist/shell-shard/toolbar/slots/FocusLockSlot.svelte.d.ts +7 -0
- package/dist/shell-shard/toolbar/slots/ModeSlot.svelte +102 -0
- package/dist/shell-shard/toolbar/slots/ModeSlot.svelte.d.ts +11 -0
- package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte +17 -0
- package/dist/shell-shard/toolbar/slots/TargetShardSlot.svelte.d.ts +6 -0
- package/dist/shell-shard/toolbar/slots.d.ts +17 -0
- package/dist/shell-shard/toolbar/slots.js +26 -0
- package/dist/shell-shard/toolbar/slots.test.d.ts +1 -0
- package/dist/shell-shard/toolbar/slots.test.js +28 -0
- package/dist/shell-shard/verbs/cat.d.ts +2 -0
- package/dist/shell-shard/verbs/cat.js +34 -0
- package/dist/shell-shard/verbs/cd.test.d.ts +1 -0
- package/dist/shell-shard/verbs/cd.test.js +56 -0
- package/dist/shell-shard/verbs/env.d.ts +2 -0
- package/dist/shell-shard/verbs/env.js +14 -0
- package/dist/shell-shard/verbs/index.js +6 -1
- package/dist/shell-shard/verbs/ls.d.ts +2 -0
- package/dist/shell-shard/verbs/ls.js +29 -0
- package/dist/shell-shard/verbs/ls.test.d.ts +1 -0
- package/dist/shell-shard/verbs/ls.test.js +49 -0
- package/dist/shell-shard/verbs/session.d.ts +0 -1
- package/dist/shell-shard/verbs/session.js +58 -26
- package/dist/verbs/types.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { App, AppManifest } from '../apps/types';
|
|
2
|
+
import type { Shard, ShardManifest } from '../shards/types';
|
|
3
|
+
import type { LayoutNode, LayoutTree, SplitNode, TabsNode, SlotNode, TabEntry } from '../layout/types';
|
|
4
|
+
export declare function makeAppManifest(overrides?: Partial<AppManifest>): AppManifest;
|
|
5
|
+
export declare function makeApp(overrides?: Partial<App>): App;
|
|
6
|
+
export declare function makeShardManifest(overrides?: Partial<ShardManifest>): ShardManifest;
|
|
7
|
+
export declare function makeShard(overrides?: Partial<Shard>): Shard;
|
|
8
|
+
export declare function makeSlotNode(slotId?: string, viewId?: string): SlotNode;
|
|
9
|
+
export declare function makeTabEntry(overrides?: Partial<TabEntry>): TabEntry;
|
|
10
|
+
export declare function makeTabsNode(tabs?: TabEntry[], overrides?: Partial<TabsNode>): TabsNode;
|
|
11
|
+
export declare function makeSplitNode(children: LayoutNode[], overrides?: Partial<SplitNode>): SplitNode;
|
|
12
|
+
export declare function makeTree(docked: LayoutNode): LayoutTree;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
let seq = 0;
|
|
2
|
+
const uid = (prefix) => `${prefix}-${++seq}`;
|
|
3
|
+
export function makeAppManifest(overrides = {}) {
|
|
4
|
+
var _a, _b, _c, _d, _e;
|
|
5
|
+
return Object.assign({ id: (_a = overrides.id) !== null && _a !== void 0 ? _a : uid('app'), label: (_b = overrides.label) !== null && _b !== void 0 ? _b : 'Test App', version: (_c = overrides.version) !== null && _c !== void 0 ? _c : '1.0.0', requiredShards: (_d = overrides.requiredShards) !== null && _d !== void 0 ? _d : [], layoutVersion: (_e = overrides.layoutVersion) !== null && _e !== void 0 ? _e : 1 }, overrides);
|
|
6
|
+
}
|
|
7
|
+
export function makeApp(overrides = {}) {
|
|
8
|
+
var _a;
|
|
9
|
+
const manifest = makeAppManifest(overrides.manifest);
|
|
10
|
+
return {
|
|
11
|
+
manifest,
|
|
12
|
+
initialLayout: (_a = overrides.initialLayout) !== null && _a !== void 0 ? _a : [
|
|
13
|
+
{ name: 'default', tree: makeTree(makeSlotNode('sh3core.home')) },
|
|
14
|
+
],
|
|
15
|
+
activate: overrides.activate,
|
|
16
|
+
deactivate: overrides.deactivate,
|
|
17
|
+
suspend: overrides.suspend,
|
|
18
|
+
resume: overrides.resume,
|
|
19
|
+
onAppReady: overrides.onAppReady,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function makeShardManifest(overrides = {}) {
|
|
23
|
+
var _a, _b, _c, _d;
|
|
24
|
+
return Object.assign({ id: (_a = overrides.id) !== null && _a !== void 0 ? _a : uid('shard'), label: (_b = overrides.label) !== null && _b !== void 0 ? _b : 'Test Shard', version: (_c = overrides.version) !== null && _c !== void 0 ? _c : '1.0.0', views: (_d = overrides.views) !== null && _d !== void 0 ? _d : [] }, overrides);
|
|
25
|
+
}
|
|
26
|
+
export function makeShard(overrides = {}) {
|
|
27
|
+
var _a;
|
|
28
|
+
return Object.assign({ manifest: makeShardManifest(overrides.manifest), activate: (_a = overrides.activate) !== null && _a !== void 0 ? _a : (() => { }), deactivate: overrides.deactivate }, overrides);
|
|
29
|
+
}
|
|
30
|
+
export function makeSlotNode(slotId, viewId) {
|
|
31
|
+
const id = slotId !== null && slotId !== void 0 ? slotId : uid('slot');
|
|
32
|
+
return { type: 'slot', slotId: id, viewId: viewId !== null && viewId !== void 0 ? viewId : 'test:view' };
|
|
33
|
+
}
|
|
34
|
+
export function makeTabEntry(overrides = {}) {
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
const slotId = (_a = overrides.slotId) !== null && _a !== void 0 ? _a : uid('tab');
|
|
37
|
+
return {
|
|
38
|
+
slotId,
|
|
39
|
+
viewId: (_b = overrides.viewId) !== null && _b !== void 0 ? _b : 'test:view',
|
|
40
|
+
label: (_c = overrides.label) !== null && _c !== void 0 ? _c : slotId,
|
|
41
|
+
icon: overrides.icon,
|
|
42
|
+
meta: overrides.meta,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function makeTabsNode(tabs = [makeTabEntry()], overrides = {}) {
|
|
46
|
+
var _a;
|
|
47
|
+
return Object.assign({ type: 'tabs', tabs, activeTab: (_a = overrides.activeTab) !== null && _a !== void 0 ? _a : 0, persistent: overrides.persistent, emptyRenderer: overrides.emptyRenderer }, overrides);
|
|
48
|
+
}
|
|
49
|
+
export function makeSplitNode(children, overrides = {}) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
return {
|
|
52
|
+
type: 'split',
|
|
53
|
+
direction: (_a = overrides.direction) !== null && _a !== void 0 ? _a : 'horizontal',
|
|
54
|
+
sizes: (_b = overrides.sizes) !== null && _b !== void 0 ? _b : children.map(() => 1 / children.length),
|
|
55
|
+
pinned: overrides.pinned,
|
|
56
|
+
collapsed: overrides.collapsed,
|
|
57
|
+
children,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function makeTree(docked) {
|
|
61
|
+
return { docked, floats: [] };
|
|
62
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RenderResult } from '@testing-library/svelte';
|
|
2
|
+
import type { Component, ComponentImport } from '@testing-library/svelte-core/types';
|
|
3
|
+
export declare function renderWithShell<C extends Component>(ComponentToRender: ComponentImport<C>, props?: Record<string, unknown>): RenderResult<C>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// packages/sh3-core/src/__test__/render.ts
|
|
2
|
+
import { render } from '@testing-library/svelte';
|
|
3
|
+
export function renderWithShell(ComponentToRender, props = {}) {
|
|
4
|
+
const host = document.createElement('div');
|
|
5
|
+
host.classList.add('sh3-shell-host');
|
|
6
|
+
host.style.position = 'relative';
|
|
7
|
+
host.style.width = '1024px';
|
|
8
|
+
host.style.height = '768px';
|
|
9
|
+
document.body.appendChild(host);
|
|
10
|
+
return render(ComponentToRender, { props, target: host });
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the framework to a deterministic boot state for tests.
|
|
3
|
+
*
|
|
4
|
+
* Order rationale:
|
|
5
|
+
* 1. Overlays and presets unbind first — they hold references into the
|
|
6
|
+
* layout store.
|
|
7
|
+
* 2. Drag state is cleared (also removes global pointer listeners).
|
|
8
|
+
* 3. Layout store clears appEntry and root — pool teardown can now run
|
|
9
|
+
* safely.
|
|
10
|
+
* 4. Slot-host pool drops all hosts.
|
|
11
|
+
* 5. View/shard/app registries empty last — shards may have contributed
|
|
12
|
+
* views.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resetFramework(): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// packages/sh3-core/src/__test__/reset.ts
|
|
2
|
+
// Central reset orchestrator. Every new test file calls this in
|
|
3
|
+
// beforeEach. Order matters — see the in-line comments.
|
|
4
|
+
import { __resetFloatManagerForTest } from '../overlays/float';
|
|
5
|
+
import { __resetPresetManagerForTest } from '../overlays/presets';
|
|
6
|
+
import { __resetDragStateForTest } from '../layout/drag.svelte';
|
|
7
|
+
import { __resetLayoutStoreForTest } from '../layout/store.svelte';
|
|
8
|
+
import { resetSlotHostPool } from '../layout/slotHostPool.svelte';
|
|
9
|
+
import { __resetViewRegistryForTest } from '../shards/registry';
|
|
10
|
+
import { __resetShardRegistryForTest } from '../shards/activate.svelte';
|
|
11
|
+
import { __resetAppRegistryForTest } from '../apps/registry.svelte';
|
|
12
|
+
/**
|
|
13
|
+
* Return the framework to a deterministic boot state for tests.
|
|
14
|
+
*
|
|
15
|
+
* Order rationale:
|
|
16
|
+
* 1. Overlays and presets unbind first — they hold references into the
|
|
17
|
+
* layout store.
|
|
18
|
+
* 2. Drag state is cleared (also removes global pointer listeners).
|
|
19
|
+
* 3. Layout store clears appEntry and root — pool teardown can now run
|
|
20
|
+
* safely.
|
|
21
|
+
* 4. Slot-host pool drops all hosts.
|
|
22
|
+
* 5. View/shard/app registries empty last — shards may have contributed
|
|
23
|
+
* views.
|
|
24
|
+
*/
|
|
25
|
+
export function resetFramework() {
|
|
26
|
+
__resetFloatManagerForTest();
|
|
27
|
+
__resetPresetManagerForTest();
|
|
28
|
+
__resetDragStateForTest();
|
|
29
|
+
__resetLayoutStoreForTest();
|
|
30
|
+
resetSlotHostPool();
|
|
31
|
+
__resetViewRegistryForTest();
|
|
32
|
+
__resetShardRegistryForTest();
|
|
33
|
+
__resetAppRegistryForTest();
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
|
2
|
+
/**
|
|
3
|
+
* happy-dom creates per-window Comment subclasses (via WindowContextClassExtender)
|
|
4
|
+
* but actually instantiates nodes from the base CommentImplementation class. This
|
|
5
|
+
* breaks Svelte 5's `instanceof Comment` check in `first_child` (operations.js),
|
|
6
|
+
* which it uses to detect and skip Svelte anchor comment nodes in templates.
|
|
7
|
+
*
|
|
8
|
+
* When `instanceof Comment` fails, `first_child` returns the anchor comment itself
|
|
9
|
+
* instead of skipping it, causing template traversal to land on the wrong sibling
|
|
10
|
+
* nodes. This cascades into split-pane snippets receiving a `null` anchor, which
|
|
11
|
+
* triggers Svelte's `invalid_snippet_arguments` error.
|
|
12
|
+
*
|
|
13
|
+
* Fix: override `Symbol.hasInstance` on the per-window `Comment` class so it falls
|
|
14
|
+
* back to a nodeType check (8 = COMMENT_NODE) when the prototype chain check fails.
|
|
15
|
+
* This is safe because nodeType 8 is exclusively Comment nodes in the DOM spec.
|
|
16
|
+
*/
|
|
17
|
+
if (typeof globalThis.Comment === 'function') {
|
|
18
|
+
Object.defineProperty(globalThis.Comment, Symbol.hasInstance, {
|
|
19
|
+
value(instance) {
|
|
20
|
+
if (instance == null)
|
|
21
|
+
return false;
|
|
22
|
+
return instance.nodeType === 8;
|
|
23
|
+
},
|
|
24
|
+
configurable: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { resetFramework } from './reset';
|
|
3
|
+
import { registeredApps, activeApp } from '../apps/registry.svelte';
|
|
4
|
+
import { registeredShards, activeShards } from '../shards/activate.svelte';
|
|
5
|
+
import { layoutStore } from '../layout/store.svelte';
|
|
6
|
+
import { makeApp, makeShard } from './fixtures';
|
|
7
|
+
import { registerApp } from '../apps/registry.svelte';
|
|
8
|
+
import { registerShard } from '../shards/activate.svelte';
|
|
9
|
+
describe('resetFramework', () => {
|
|
10
|
+
beforeEach(resetFramework);
|
|
11
|
+
it('returns all registries and the layout store to boot state', () => {
|
|
12
|
+
registerApp(makeApp());
|
|
13
|
+
registerShard(makeShard());
|
|
14
|
+
activeApp.id = 'dirty';
|
|
15
|
+
expect(registeredApps.size).toBeGreaterThan(0);
|
|
16
|
+
expect(registeredShards.size).toBeGreaterThan(0);
|
|
17
|
+
resetFramework();
|
|
18
|
+
expect(registeredApps.size).toBe(0);
|
|
19
|
+
expect(registeredShards.size).toBe(0);
|
|
20
|
+
expect(activeShards.size).toBe(0);
|
|
21
|
+
expect(activeApp.id).toBeNull();
|
|
22
|
+
expect(layoutStore.root).toEqual({
|
|
23
|
+
type: 'slot',
|
|
24
|
+
slotId: 'sh3core.home',
|
|
25
|
+
viewId: 'sh3core:home',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
package/dist/api.d.ts
CHANGED
|
@@ -3,16 +3,26 @@ export type { Shell } from './shellRuntime.svelte';
|
|
|
3
3
|
export type { Shard, ShardManifest, SourceShard, SourceShardManifest, ShardContext, ViewDeclaration, ViewFactory, ViewHandle, MountContext, } from './shards/types';
|
|
4
4
|
export type { LayoutNode, SplitNode, TabsNode, SlotNode, TabEntry, SplitDirection, SizeMode, LayoutTree, FloatEntry, LayoutPreset, CanonicalPreset, } from './layout/types';
|
|
5
5
|
export type { FloatManager, FloatOptions } from './overlays/float';
|
|
6
|
+
export type { ModalManager } from './overlays/modal';
|
|
7
|
+
export type { PopupManager } from './overlays/popup';
|
|
8
|
+
export type { ToastManager } from './overlays/toast';
|
|
6
9
|
export type { PresetManager } from './overlays/presets';
|
|
10
|
+
export type { OverlayHandle, ModalHandle, ModalOptions, PopupHandle, PopupOptions, PopupPlacement, ToastHandle, ToastOptions, ToastLevel, } from './overlays/types';
|
|
7
11
|
export type { ZoneSchema, ZoneName, ZoneManager } from './state/types';
|
|
8
12
|
export { PERMISSION_STATE_MANAGE } from './state/types';
|
|
9
13
|
export type { StateZones } from './state/zones.svelte';
|
|
10
14
|
export type { EnvState } from './env/types';
|
|
11
15
|
export type { App, AppManifest, SourceApp, SourceAppManifest, AppContext, } from './apps/types';
|
|
12
16
|
export { listRegisteredApps, getActiveApp } from './apps/registry.svelte';
|
|
13
|
-
export { launchApp, returnToHome } from './apps/lifecycle';
|
|
17
|
+
export { launchApp, returnToHome, unregisterApp } from './apps/lifecycle';
|
|
14
18
|
export { inspectActiveLayout, spliceIntoActiveLayout, dockIntoActiveLayout, focusTab, focusView, collapseChild, expandChild, closeTab, } from './layout/inspection';
|
|
15
19
|
export type { DocumentHandle, DocumentHandleOptions, DocumentFormat, DocumentMeta, DocumentChange, AutosaveController, } from './documents/types';
|
|
20
|
+
export type { SyncHandle, SyncScope, ManifestEntry, ApplyEntry, ApplyOpts, ApplyOutcome, ApplyBatchResult, ConflictPolicy, ConflictResolution, ConflictContext, JournalEntry, ChangePage, GrantRecord, } from './documents/sync/types';
|
|
21
|
+
export { PERMISSION_DOCUMENTS_SYNC, ScopeNotGrantedError, ScopeRevokedError, TenantMismatchError, } from './documents/sync/types';
|
|
22
|
+
export { createSyncRegistry } from './documents/sync/registry';
|
|
23
|
+
export type { SyncRegistry } from './documents/sync/registry';
|
|
24
|
+
export { default as SyncGrantPicker } from './documents/sync/components/SyncGrantPicker.svelte';
|
|
25
|
+
export { default as DocumentSyncExplorer } from './documents/sync/components/DocumentSyncExplorer.svelte';
|
|
16
26
|
export { registeredShards, activeShards } from './shards/activate.svelte';
|
|
17
27
|
export type { RegistryIndex, PackageEntry, PackageVersion, RequiredDependency, InstalledPackage, InstallResult, PackageMeta, } from './registry/types';
|
|
18
28
|
export type { ResolvedPackage } from './registry/client';
|
|
@@ -26,7 +36,10 @@ export declare const capabilities: {
|
|
|
26
36
|
readonly hotInstall: boolean;
|
|
27
37
|
};
|
|
28
38
|
export type { ServerShard, ServerShardContext } from './server-shard/types';
|
|
29
|
-
export type { Verb } from './verbs/types';
|
|
39
|
+
export type { Verb, VerbContext, ShellApi } from './verbs/types';
|
|
40
|
+
export type { Scrollback } from './shell-shard/scrollback.svelte';
|
|
41
|
+
export type { SessionClient } from './shell-shard/session-client.svelte';
|
|
30
42
|
export { listVerbs } from './shards/registry';
|
|
31
43
|
export { VERSION } from './version';
|
|
44
|
+
export declare const FRAMEWORK_SHARD_IDS: readonly string[];
|
|
32
45
|
export { setTokenOverrides, clearTokenOverrides, getTokenOverrides, } from './theme';
|
package/dist/api.js
CHANGED
|
@@ -26,9 +26,13 @@ export { shell } from './shellRuntime.svelte';
|
|
|
26
26
|
export { PERMISSION_STATE_MANAGE } from './state/types';
|
|
27
27
|
// Host actions callable from inside views (shell home, status bar, etc.).
|
|
28
28
|
export { listRegisteredApps, getActiveApp } from './apps/registry.svelte';
|
|
29
|
-
export { launchApp, returnToHome } from './apps/lifecycle';
|
|
29
|
+
export { launchApp, returnToHome, unregisterApp } from './apps/lifecycle';
|
|
30
30
|
// Layout inspection / mutation for advanced shards (diagnostic, etc.).
|
|
31
31
|
export { inspectActiveLayout, spliceIntoActiveLayout, dockIntoActiveLayout, focusTab, focusView, collapseChild, expandChild, closeTab, } from './layout/inspection';
|
|
32
|
+
export { PERMISSION_DOCUMENTS_SYNC, ScopeNotGrantedError, ScopeRevokedError, TenantMismatchError, } from './documents/sync/types';
|
|
33
|
+
export { createSyncRegistry } from './documents/sync/registry';
|
|
34
|
+
export { default as SyncGrantPicker } from './documents/sync/components/SyncGrantPicker.svelte';
|
|
35
|
+
export { default as DocumentSyncExplorer } from './documents/sync/components/DocumentSyncExplorer.svelte';
|
|
32
36
|
// Shard introspection — read-only reactive maps exposing which shards are
|
|
33
37
|
// known to the host and which are currently active. Intended for diagnostic
|
|
34
38
|
// and tooling shards that need to visualize framework state. Phase 9
|
|
@@ -47,5 +51,13 @@ export const capabilities = {
|
|
|
47
51
|
export { listVerbs } from './shards/registry';
|
|
48
52
|
// Package version.
|
|
49
53
|
export { VERSION } from './version';
|
|
54
|
+
// Framework shard IDs — shards that are always present (built-in to sh3-core).
|
|
55
|
+
// Used by the store UI to skip dependency checks for these well-known shards.
|
|
56
|
+
export const FRAMEWORK_SHARD_IDS = [
|
|
57
|
+
'__sh3core__',
|
|
58
|
+
'shell',
|
|
59
|
+
'sh3-store',
|
|
60
|
+
'sh3-admin',
|
|
61
|
+
];
|
|
50
62
|
// Theme token override API (shell-level theming support).
|
|
51
63
|
export { setTokenOverrides, clearTokenOverrides, getTokenOverrides, } from './theme';
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { contract } from '../../contract';
|
|
14
14
|
import type { ResolvedPackage } from '../../registry/client';
|
|
15
15
|
import type { InstalledPackage } from '../../registry/types';
|
|
16
|
+
import { FRAMEWORK_SHARD_IDS } from '../../api';
|
|
16
17
|
|
|
17
18
|
let search = $state('');
|
|
18
19
|
let typeFilter = $state<'all' | 'shard' | 'app'>('all');
|
|
@@ -75,6 +76,16 @@
|
|
|
75
76
|
return ctx.state.ephemeral.installed.find((p: InstalledPackage) => p.id === id)?.version ?? '';
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
function missingShards(pkg: ResolvedPackage, installed: InstalledPackage[]): string[] {
|
|
80
|
+
const required = pkg.latest.requires?.map((r) => r.id) ?? [];
|
|
81
|
+
if (required.length === 0) return [];
|
|
82
|
+
const known = new Set<string>(FRAMEWORK_SHARD_IDS);
|
|
83
|
+
for (const p of installed) {
|
|
84
|
+
if (p.type === 'shard' || p.type === 'combo') known.add(p.id);
|
|
85
|
+
}
|
|
86
|
+
return required.filter((id: string) => !known.has(id));
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
async function handleUpdate(id: string) {
|
|
79
90
|
if (updatingIds.has(id)) return;
|
|
80
91
|
|
|
@@ -118,6 +129,7 @@
|
|
|
118
129
|
version: meta.version,
|
|
119
130
|
contractVersion: meta.contractVersion,
|
|
120
131
|
sourceRegistry: meta.sourceRegistry,
|
|
132
|
+
requiredShards: pkg.latest.requires?.map((r) => r.id) ?? [],
|
|
121
133
|
installedAt: new Date().toISOString(),
|
|
122
134
|
};
|
|
123
135
|
const serverResult = await serverInstallPackage(manifest, bundle, serverBundle);
|
|
@@ -215,6 +227,7 @@
|
|
|
215
227
|
{@const installing = installingIds.has(pkg.entry.id)}
|
|
216
228
|
{@const updatable = hasUpdate(pkg.entry.id)}
|
|
217
229
|
{@const updating = updatingIds.has(pkg.entry.id)}
|
|
230
|
+
{@const missing = missingShards(pkg, ctx.state.ephemeral.installed)}
|
|
218
231
|
<div class="store-card">
|
|
219
232
|
<div class="store-card-header">
|
|
220
233
|
<div class="store-card-icon">
|
|
@@ -253,13 +266,19 @@
|
|
|
253
266
|
{:else if installed}
|
|
254
267
|
<span class="store-installed-label">Installed</span>
|
|
255
268
|
{:else}
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
269
|
+
<div class="store-install-wrap">
|
|
270
|
+
<button
|
|
271
|
+
class="store-install-btn"
|
|
272
|
+
onclick={() => handleInstall(pkg)}
|
|
273
|
+
disabled={installing || missing.length > 0}
|
|
274
|
+
title={missing.length > 0 ? `Missing required shards: ${missing.join(', ')}` : undefined}
|
|
275
|
+
>
|
|
276
|
+
{installing ? 'Installing...' : 'Install'}
|
|
277
|
+
</button>
|
|
278
|
+
{#if missing.length > 0}
|
|
279
|
+
<span class="store-card-missing">requires: {missing.join(', ')}</span>
|
|
280
|
+
{/if}
|
|
281
|
+
</div>
|
|
263
282
|
{/if}
|
|
264
283
|
</div>
|
|
265
284
|
</div>
|
|
@@ -449,6 +468,16 @@
|
|
|
449
468
|
color: var(--shell-success, #4caf50);
|
|
450
469
|
font-weight: 600;
|
|
451
470
|
}
|
|
471
|
+
.store-install-wrap {
|
|
472
|
+
display: flex;
|
|
473
|
+
flex-direction: column;
|
|
474
|
+
align-items: flex-end;
|
|
475
|
+
gap: 4px;
|
|
476
|
+
}
|
|
477
|
+
.store-card-missing {
|
|
478
|
+
font-size: 0.75rem;
|
|
479
|
+
color: var(--shell-warning, #ff9800);
|
|
480
|
+
}
|
|
452
481
|
.store-update-btn {
|
|
453
482
|
padding: 5px 14px;
|
|
454
483
|
background: var(--shell-warning, #ff9800);
|
|
@@ -134,7 +134,7 @@ export const storeShard = {
|
|
|
134
134
|
await ctx.envUpdate({ registries });
|
|
135
135
|
}
|
|
136
136
|
async function updatePackage(id) {
|
|
137
|
-
var _a, _b;
|
|
137
|
+
var _a, _b, _c, _d;
|
|
138
138
|
const catalogEntry = state.ephemeral.updatable[id];
|
|
139
139
|
if (!catalogEntry)
|
|
140
140
|
return;
|
|
@@ -160,10 +160,16 @@ export const storeShard = {
|
|
|
160
160
|
contractVersion: meta.contractVersion,
|
|
161
161
|
sourceRegistry: meta.sourceRegistry,
|
|
162
162
|
installedAt: new Date().toISOString(),
|
|
163
|
+
requiredShards: (_b = (_a = catalogEntry.latest.requires) === null || _a === void 0 ? void 0 : _a.map((r) => r.id)) !== null && _b !== void 0 ? _b : [],
|
|
163
164
|
};
|
|
164
165
|
const serverResult = await serverInstallPackage(manifest, bundle, serverBundle);
|
|
165
166
|
if (!serverResult.ok) {
|
|
166
|
-
|
|
167
|
+
let message = (_c = serverResult.error) !== null && _c !== void 0 ? _c : 'Server update failed';
|
|
168
|
+
if (serverResult.code === 'missing-shards' && serverResult.missing) {
|
|
169
|
+
const ids = serverResult.missing.map((m) => m.id).join(', ');
|
|
170
|
+
message = `missing required shard(s): ${ids}`;
|
|
171
|
+
}
|
|
172
|
+
throw new Error(message);
|
|
167
173
|
}
|
|
168
174
|
// 4. Install locally (overwrites IndexedDB + re-registers).
|
|
169
175
|
const result = await installPackage(bundle, meta);
|
|
@@ -177,7 +183,7 @@ export const storeShard = {
|
|
|
177
183
|
console.warn(`[sh3-store] Rollback failed for "${id}":`, rollbackErr instanceof Error ? rollbackErr.message : rollbackErr);
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
|
-
throw new Error((
|
|
186
|
+
throw new Error((_d = result.error) !== null && _d !== void 0 ? _d : 'Local install failed during update');
|
|
181
187
|
}
|
|
182
188
|
await refreshInstalled();
|
|
183
189
|
}
|
package/dist/app/store/verbs.js
CHANGED
|
@@ -19,7 +19,7 @@ export const installVerb = {
|
|
|
19
19
|
name: 'install',
|
|
20
20
|
summary: 'Install a package by id from the catalog.',
|
|
21
21
|
async run(ctx, args) {
|
|
22
|
-
var _a;
|
|
22
|
+
var _a, _b, _c;
|
|
23
23
|
const id = args[0];
|
|
24
24
|
if (!id) {
|
|
25
25
|
ctx.scrollback.push({
|
|
@@ -71,12 +71,18 @@ export const installVerb = {
|
|
|
71
71
|
contractVersion: meta.contractVersion,
|
|
72
72
|
sourceRegistry: meta.sourceRegistry,
|
|
73
73
|
installedAt: new Date().toISOString(),
|
|
74
|
+
requiredShards: (_b = (_a = pkg.latest.requires) === null || _a === void 0 ? void 0 : _a.map((r) => r.id)) !== null && _b !== void 0 ? _b : [],
|
|
74
75
|
};
|
|
75
76
|
const serverResult = await serverInstallPackage(manifest, bundle, serverBundle);
|
|
76
77
|
if (!serverResult.ok) {
|
|
78
|
+
let text = `install failed: ${(_c = serverResult.error) !== null && _c !== void 0 ? _c : 'server error'}`;
|
|
79
|
+
if (serverResult.code === 'missing-shards' && serverResult.missing) {
|
|
80
|
+
const ids = serverResult.missing.map((m) => m.id).join(', ');
|
|
81
|
+
text = `install failed: missing required shard(s): ${ids}`;
|
|
82
|
+
}
|
|
77
83
|
ctx.scrollback.push({
|
|
78
84
|
kind: 'status',
|
|
79
|
-
text
|
|
85
|
+
text,
|
|
80
86
|
level: 'error',
|
|
81
87
|
ts: Date.now(),
|
|
82
88
|
});
|
package/dist/apps/lifecycle.d.ts
CHANGED
|
@@ -25,6 +25,17 @@ export declare function launchApp(id: string): Promise<void>;
|
|
|
25
25
|
* @param id - The `AppManifest.id` of the app to unload.
|
|
26
26
|
*/
|
|
27
27
|
export declare function unloadApp(id: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Unregister an app and remove it from the registry.
|
|
30
|
+
*
|
|
31
|
+
* If the app is currently active, force-closes (android-style): calls
|
|
32
|
+
* `App.deactivate`, detaches the layout, and deactivates required shards,
|
|
33
|
+
* then clears the active-app pointer. Unlike `returnToHome` this does not
|
|
34
|
+
* run suspend hooks and cannot be cancelled.
|
|
35
|
+
*
|
|
36
|
+
* No-op if the id is not registered.
|
|
37
|
+
*/
|
|
38
|
+
export declare function unregisterApp(id: string): void;
|
|
28
39
|
/**
|
|
29
40
|
* Return to the shell home view without unloading the active app. The
|
|
30
41
|
* app's shards stay running, its layout proxy stays attached with its
|
package/dist/apps/lifecycle.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { createStateZones } from '../state/zones.svelte';
|
|
15
15
|
import { activateShard, deactivateShard, getShardContext, registeredShards, } from '../shards/activate.svelte';
|
|
16
|
-
import { attachApp, detachApp, switchToApp, switchToHome, } from '../layout/store.svelte';
|
|
17
|
-
import { activeApp, getRegisteredApp } from './registry.svelte';
|
|
16
|
+
import { attachApp, acquireAppSlotHolds, detachApp, switchToApp, switchToHome, } from '../layout/store.svelte';
|
|
17
|
+
import { activeApp, getRegisteredApp, registeredApps } from './registry.svelte';
|
|
18
18
|
import { createZoneManager } from '../state/manage';
|
|
19
19
|
import { PERMISSION_STATE_MANAGE } from '../state/types';
|
|
20
20
|
// ---------- last-active-app user zone ------------------------------------
|
|
@@ -69,7 +69,7 @@ function getOrCreateAppContext(appId) {
|
|
|
69
69
|
* @throws If the app is not registered or a required shard is not registered.
|
|
70
70
|
*/
|
|
71
71
|
export async function launchApp(id) {
|
|
72
|
-
var _a, _b, _c;
|
|
72
|
+
var _a, _b, _c, _d, _e;
|
|
73
73
|
const app = getRegisteredApp(id);
|
|
74
74
|
if (!app) {
|
|
75
75
|
throw new Error(`Cannot launch app "${id}": not registered`);
|
|
@@ -91,25 +91,42 @@ export async function launchApp(id) {
|
|
|
91
91
|
}
|
|
92
92
|
void ((_b = app.resume) === null || _b === void 0 ? void 0 : _b.call(app, getOrCreateAppContext(id)));
|
|
93
93
|
switchToApp();
|
|
94
|
+
void ((_c = app.onAppReady) === null || _c === void 0 ? void 0 : _c.call(app, getOrCreateAppContext(id)));
|
|
94
95
|
writeLastApp(id);
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
// unconditionally.
|
|
98
|
+
// Validate required shards are registered before attaching anything,
|
|
99
|
+
// so we don't half-attach and leave presetManager bound to a blob for
|
|
100
|
+
// an app whose shards we can't even find.
|
|
101
101
|
for (const shardId of app.manifest.requiredShards) {
|
|
102
102
|
if (!registeredShards.has(shardId)) {
|
|
103
103
|
throw new Error(`App "${id}" requires shard "${shardId}" which is not registered`);
|
|
104
104
|
}
|
|
105
|
-
await activateShard(shardId);
|
|
106
105
|
}
|
|
107
|
-
// Attach the layout
|
|
108
|
-
//
|
|
106
|
+
// Attach the layout before activating shards so `presetManager` is
|
|
107
|
+
// bound to this app's AppLayoutBlob during shard activation. Shards
|
|
108
|
+
// legitimately read/switch presets from their activate() hook; the
|
|
109
|
+
// previous order (shards first, attach after) made any such call
|
|
110
|
+
// throw "no app attached". If shard activation fails below, we
|
|
111
|
+
// detach to keep the preset manager state consistent.
|
|
109
112
|
attachApp(app);
|
|
110
|
-
|
|
113
|
+
try {
|
|
114
|
+
for (const shardId of app.manifest.requiredShards) {
|
|
115
|
+
await activateShard(shardId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
detachApp();
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
// Shards have registered their view factories — safe to take the
|
|
123
|
+
// refcount holds on the app's slots now (pool's factory lookup
|
|
124
|
+
// happens in a microtask from this call).
|
|
125
|
+
acquireAppSlotHolds();
|
|
126
|
+
void ((_d = app.activate) === null || _d === void 0 ? void 0 : _d.call(app, getOrCreateAppContext(id)));
|
|
111
127
|
activeApp.id = id;
|
|
112
128
|
switchToApp();
|
|
129
|
+
void ((_e = app.onAppReady) === null || _e === void 0 ? void 0 : _e.call(app, getOrCreateAppContext(id)));
|
|
113
130
|
writeLastApp(id);
|
|
114
131
|
}
|
|
115
132
|
// ---------- unload --------------------------------------------------------
|
|
@@ -152,6 +169,26 @@ export function unloadApp(id) {
|
|
|
152
169
|
activeApp.id = null;
|
|
153
170
|
appContexts.delete(id);
|
|
154
171
|
}
|
|
172
|
+
// ---------- unregister -------------------------------------------------------
|
|
173
|
+
/**
|
|
174
|
+
* Unregister an app and remove it from the registry.
|
|
175
|
+
*
|
|
176
|
+
* If the app is currently active, force-closes (android-style): calls
|
|
177
|
+
* `App.deactivate`, detaches the layout, and deactivates required shards,
|
|
178
|
+
* then clears the active-app pointer. Unlike `returnToHome` this does not
|
|
179
|
+
* run suspend hooks and cannot be cancelled.
|
|
180
|
+
*
|
|
181
|
+
* No-op if the id is not registered.
|
|
182
|
+
*/
|
|
183
|
+
export function unregisterApp(id) {
|
|
184
|
+
if (!registeredApps.has(id))
|
|
185
|
+
return;
|
|
186
|
+
if (activeApp.id === id) {
|
|
187
|
+
unloadApp(id);
|
|
188
|
+
writeLastApp(null);
|
|
189
|
+
}
|
|
190
|
+
registeredApps.delete(id);
|
|
191
|
+
}
|
|
155
192
|
// ---------- return to home -----------------------------------------------
|
|
156
193
|
/**
|
|
157
194
|
* Return to the shell home view without unloading the active app. The
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|