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.
- package/dist/__test__/fixtures.js +1 -1
- package/dist/__test__/reset.js +1 -3
- package/dist/__test__/smoke.test.js +2 -2
- package/dist/actions/contextMenuModel.test.js +6 -3
- package/dist/actions/ctx-actions.svelte.test.js +9 -9
- package/dist/actions/dispatcher-v3.test.js +8 -0
- package/dist/actions/dispatcher.svelte.d.ts +1 -2
- package/dist/actions/dispatcher.svelte.js +6 -7
- package/dist/actions/dispatcher.test.js +9 -12
- package/dist/actions/listActionsFromEntries.test.js +1 -2
- package/dist/actions/listActive.test.js +2 -3
- package/dist/actions/menuBarModel.test.js +1 -7
- package/dist/actions/paletteModel.test.js +1 -3
- package/dist/actions/scope-helpers.test.js +4 -4
- package/dist/actions/shardContext.test.js +2 -2
- package/dist/actions/state.svelte.d.ts +12 -2
- package/dist/actions/state.svelte.js +15 -12
- package/dist/actions/state.test.js +4 -4
- package/dist/api.d.ts +4 -3
- package/dist/api.js +1 -1
- package/dist/app/admin/adminShard.svelte.js +1 -1
- package/dist/app/store/storeShard.svelte.js +10 -5
- package/dist/app-appearance/appearanceShard.svelte.js +1 -5
- package/dist/apps/lifecycle.js +49 -64
- package/dist/apps/lifecycle.test.js +30 -76
- package/dist/conflicts/adapter-documents.js +1 -2
- package/dist/createShell.js +1 -1
- package/dist/documents/handle.d.ts +9 -4
- package/dist/documents/handle.js +40 -29
- package/dist/documents/handle.test.js +60 -51
- package/dist/documents/index.d.ts +1 -1
- package/dist/documents/types.d.ts +16 -26
- package/dist/host.d.ts +1 -1
- package/dist/host.js +9 -56
- package/dist/host.svelte.test.js +31 -63
- package/dist/layouts-shard/LayoutsSection.svelte +1 -1
- package/dist/layouts-shard/layoutsShard.svelte.js +2 -5
- package/dist/layouts-shard/layoutsShard.svelte.test.js +2 -2
- package/dist/projects-shard/projectsShard.svelte.js +1 -5
- package/dist/registry/installer.js +1 -1
- package/dist/registry/loader.d.ts +1 -1
- package/dist/registry/loader.js +3 -3
- package/dist/registry/permission-descriptions.test.js +2 -2
- package/dist/registry/register.js +1 -1
- package/dist/registry/register.test.js +1 -1
- package/dist/runtime/runVerb-shell.test.js +1 -1
- package/dist/runtime/runVerb.js +2 -2
- package/dist/runtime/runVerb.test.js +9 -9
- package/dist/server-shard/types.d.ts +56 -0
- package/dist/sh3Api/headless.js +1 -1
- package/dist/sh3core-shard/sh3coreShard.svelte.js +1 -6
- package/dist/shards/ctx-fetch.test.js +9 -9
- package/dist/shards/lifecycle.svelte.d.ts +108 -0
- package/dist/shards/lifecycle.svelte.js +551 -0
- package/dist/shards/lifecycle.test.js +139 -0
- package/dist/shards/types.d.ts +30 -63
- package/dist/shell-shard/shellShard.svelte.js +1 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/shards/activate-browse.test.js +0 -120
- package/dist/shards/activate-contributions.test.js +0 -141
- package/dist/shards/activate-error-isolation.test.d.ts +0 -1
- package/dist/shards/activate-error-isolation.test.js +0 -98
- package/dist/shards/activate-fields.svelte.test.d.ts +0 -1
- package/dist/shards/activate-fields.svelte.test.js +0 -121
- package/dist/shards/activate-on-key-revoked.test.d.ts +0 -1
- package/dist/shards/activate-on-key-revoked.test.js +0 -60
- package/dist/shards/activate-runtime.test.d.ts +0 -1
- package/dist/shards/activate-runtime.test.js +0 -344
- package/dist/shards/activate-scopeid.test.d.ts +0 -1
- package/dist/shards/activate-scopeid.test.js +0 -21
- package/dist/shards/activate.svelte.d.ts +0 -102
- package/dist/shards/activate.svelte.js +0 -407
- package/dist/shards/app-binding.svelte.d.ts +0 -8
- package/dist/shards/app-binding.svelte.js +0 -30
- package/dist/shards/app-binding.test.d.ts +0 -1
- package/dist/shards/app-binding.test.js +0 -25
- /package/dist/{shards/activate-browse.test.d.ts → actions/dispatcher-v3.test.d.ts} +0 -0
- /package/dist/shards/{activate-contributions.test.d.ts → lifecycle.test.d.ts} +0 -0
|
@@ -40,7 +40,7 @@ describe('describePermission — documents:read / documents:write', () => {
|
|
|
40
40
|
function shardWithPerms(id, perms) {
|
|
41
41
|
return {
|
|
42
42
|
manifest: { id, label: id, version: '0.0.0', views: [], permissions: perms },
|
|
43
|
-
|
|
43
|
+
register: () => { },
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
function appWithPerms(id, perms) {
|
|
@@ -54,7 +54,7 @@ function appWithPerms(id, perms) {
|
|
|
54
54
|
layoutVersion: 1,
|
|
55
55
|
permissions: perms,
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
register: () => { },
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
describe('extractBundlePermissions', () => {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* manifests; the authoritative value comes from the persisted/server
|
|
9
9
|
* metadata and must be stamped here before any consumer reads the manifest.
|
|
10
10
|
*/
|
|
11
|
-
import { registerShard } from '../shards/
|
|
11
|
+
import { registerShard } from '../shards/lifecycle.svelte';
|
|
12
12
|
import { registerApp } from '../apps/registry.svelte';
|
|
13
13
|
export function registerLoadedBundle(loaded, meta) {
|
|
14
14
|
for (const shard of loaded.shards) {
|
|
@@ -3,7 +3,7 @@ import { resetFramework } from '../__test__/reset';
|
|
|
3
3
|
import { makeApp, makeAppManifest, makeShard, makeShardManifest } from '../__test__/fixtures';
|
|
4
4
|
import { registerLoadedBundle } from './register';
|
|
5
5
|
import { registeredApps } from '../apps/registry.svelte';
|
|
6
|
-
import { registeredShards } from '../shards/
|
|
6
|
+
import { registeredShards } from '../shards/lifecycle.svelte';
|
|
7
7
|
describe('registerLoadedBundle', () => {
|
|
8
8
|
beforeEach(resetFramework);
|
|
9
9
|
it('stamps meta.version onto every shard manifest before registering', () => {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
13
13
|
import { MemoryDocumentBackend } from '../documents/backends';
|
|
14
14
|
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
15
|
-
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/
|
|
15
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/lifecycle.svelte';
|
|
16
16
|
import { __resetViewRegistryForTest } from '../shards/registry';
|
|
17
17
|
import { __resetActionsRegistryForTest } from '../actions/registry';
|
|
18
18
|
import { runVerbProgrammatic } from './runVerb';
|
package/dist/runtime/runVerb.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* runVerbProgrammatic — programmatic verb dispatch with synthesized VerbContext.
|
|
3
3
|
*
|
|
4
|
-
* Used by `ctx.runVerb(...)` (see shards/
|
|
4
|
+
* Used by `ctx.runVerb(...)` (see shards/lifecycle.svelte.ts). Builds a
|
|
5
5
|
* sink scrollback that captures entries into an array, a headless Sh3Api
|
|
6
6
|
* (no terminal-bound state), a stub SessionClient, and a real TenantFsClient.
|
|
7
7
|
* Verbs that opt in via `programmatic: true` run against this synthesized
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Resolution: prefixed names (`'sh3-store:install'`) look up directly;
|
|
16
16
|
* unprefixed sh3 names (`'apps'`) resolve against shardId 'sh3'.
|
|
17
17
|
*/
|
|
18
|
-
import { activeShards } from '../shards/
|
|
18
|
+
import { activeShards } from '../shards/lifecycle.svelte';
|
|
19
19
|
import { getVerb, listVerbsWithShard } from '../shards/registry';
|
|
20
20
|
import { makeSh3Api } from '../sh3Api/headless';
|
|
21
21
|
import { Scrollback } from '../shell-shard/scrollback.svelte';
|
|
@@ -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/
|
|
4
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/lifecycle.svelte';
|
|
5
5
|
import { __resetViewRegistryForTest } from '../shards/registry';
|
|
6
6
|
import { runVerbProgrammatic } from './runVerb';
|
|
7
7
|
function makeVerb(name, programmatic, body = async () => undefined) {
|
|
@@ -27,7 +27,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
27
27
|
it('rejects on unknown verb', async () => {
|
|
28
28
|
registerShard({
|
|
29
29
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
30
|
-
|
|
30
|
+
register(ctx) {
|
|
31
31
|
ctx.registerVerb(makeVerb('echo', true));
|
|
32
32
|
},
|
|
33
33
|
});
|
|
@@ -37,7 +37,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
37
37
|
it('rejects when verb is not programmatic', async () => {
|
|
38
38
|
registerShard({
|
|
39
39
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
40
|
-
|
|
40
|
+
register(ctx) {
|
|
41
41
|
ctx.registerVerb(makeVerb('plain', false));
|
|
42
42
|
},
|
|
43
43
|
});
|
|
@@ -47,7 +47,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
47
47
|
it('invokes a programmatic verb and resolves with { result, scrollback }', async () => {
|
|
48
48
|
registerShard({
|
|
49
49
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
50
|
-
|
|
50
|
+
register(ctx) {
|
|
51
51
|
ctx.registerVerb(makeVerb('echo', true, async (vctx, args) => {
|
|
52
52
|
vctx.scrollback.push({
|
|
53
53
|
kind: 'status',
|
|
@@ -71,7 +71,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
71
71
|
let observed = undefined;
|
|
72
72
|
registerShard({
|
|
73
73
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
74
|
-
|
|
74
|
+
register(ctx) {
|
|
75
75
|
ctx.registerVerb(makeVerb('capture', true, async (vctx) => {
|
|
76
76
|
observed = vctx.structuredArgs;
|
|
77
77
|
}));
|
|
@@ -85,7 +85,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
85
85
|
let received;
|
|
86
86
|
registerShard({
|
|
87
87
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
88
|
-
|
|
88
|
+
register(ctx) {
|
|
89
89
|
ctx.registerVerb(makeVerb('peek', true, async (vctx) => {
|
|
90
90
|
received = vctx.signal;
|
|
91
91
|
}));
|
|
@@ -99,7 +99,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
99
99
|
it('inner dispatch re-enters runVerb and merges scrollback into the outer capture', async () => {
|
|
100
100
|
registerShard({
|
|
101
101
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
102
|
-
|
|
102
|
+
register(ctx) {
|
|
103
103
|
ctx.registerVerb(makeVerb('inner', true, async (vctx) => {
|
|
104
104
|
vctx.scrollback.push({ kind: 'status', text: 'inner-fired', level: 'info', ts: 0 });
|
|
105
105
|
}));
|
|
@@ -120,7 +120,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
120
120
|
it('propagates an error thrown by the verb', async () => {
|
|
121
121
|
registerShard({
|
|
122
122
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
123
|
-
|
|
123
|
+
register(ctx) {
|
|
124
124
|
ctx.registerVerb(makeVerb('boom', true, async () => {
|
|
125
125
|
throw new Error('kaboom');
|
|
126
126
|
}));
|
|
@@ -133,7 +133,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
133
133
|
let observed = undefined;
|
|
134
134
|
registerShard({
|
|
135
135
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
136
|
-
|
|
136
|
+
register(ctx) {
|
|
137
137
|
ctx.registerVerb(makeVerb('peek-sh3', true, async (vctx) => {
|
|
138
138
|
observed = vctx.sh3.dispatchToTerminal('foo');
|
|
139
139
|
}));
|
|
@@ -12,6 +12,19 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { DocumentMeta } from '../documents/types';
|
|
14
14
|
import type { SyncPolicy, ConflictFile } from '../documents/sync-types';
|
|
15
|
+
/** Public shape of an API key — never includes the secret value. */
|
|
16
|
+
export interface ApiKeyPublic {
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
scopeId: string | null;
|
|
20
|
+
ownerUserId: string | null;
|
|
21
|
+
mintedByShardId: string | null;
|
|
22
|
+
scopes: string[];
|
|
23
|
+
peerRole?: 'primary' | 'replica';
|
|
24
|
+
peerId?: string;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
expiresAt?: string;
|
|
27
|
+
}
|
|
15
28
|
/**
|
|
16
29
|
* Per-tenant document API exposed to server shards via
|
|
17
30
|
* `ServerShardContext.documents(tenantId)`. Every method is
|
|
@@ -95,6 +108,49 @@ export interface ServerShardContext {
|
|
|
95
108
|
* Absent => 'primary' behavior at the store.
|
|
96
109
|
*/
|
|
97
110
|
setPeerRole(tenant: string, role: 'primary' | 'replica'): void;
|
|
111
|
+
/**
|
|
112
|
+
* Redeem a consent ticket and return the freshly-minted key.
|
|
113
|
+
*
|
|
114
|
+
* The browser side of your shard calls `POST /api/keys/consent` to issue
|
|
115
|
+
* a ticket bound to the user's session. The server side hands the
|
|
116
|
+
* ticket to this method to mint the key. The ticket carries the
|
|
117
|
+
* `(scopeId, userId, shardId, label, scopes, expiresIn?)` from consent
|
|
118
|
+
* — none of those are caller-controlled here, so a shard cannot escalate
|
|
119
|
+
* its own scopes or mint for a different tenant.
|
|
120
|
+
*
|
|
121
|
+
* The caller's `scopeId` is read from the Hono request context (`c`) and
|
|
122
|
+
* must match the scope recorded on the ticket; mismatches return `null`
|
|
123
|
+
* (same as expired / unknown ticket — probe-resistant).
|
|
124
|
+
*
|
|
125
|
+
* The returned `key` is the full secret string. Return it to the user
|
|
126
|
+
* exactly once and never persist it server-side; subsequent calls only
|
|
127
|
+
* see `key.id` via `listKeys`.
|
|
128
|
+
*
|
|
129
|
+
* @param c The Hono request context for the route handler.
|
|
130
|
+
* @param ticket The opaque token issued by /api/keys/consent.
|
|
131
|
+
* @returns `{ id, key }` on success, `null` on invalid / expired / scope-mismatch.
|
|
132
|
+
*/
|
|
133
|
+
redeemKeyTicket(c: unknown, ticket: string): Promise<{
|
|
134
|
+
id: string;
|
|
135
|
+
key: string;
|
|
136
|
+
} | null>;
|
|
137
|
+
/**
|
|
138
|
+
* List API keys this shard has minted for the calling caller's scope.
|
|
139
|
+
* Filtered to `mintedByShardId === ctx.shardId`. Other shards' keys are
|
|
140
|
+
* invisible. Returns an empty array if the caller has no scope.
|
|
141
|
+
*/
|
|
142
|
+
listKeys(c: unknown): Promise<ApiKeyPublic[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Revoke an API key by id. Only revokes keys minted by this shard
|
|
145
|
+
* (`mintedByShardId === ctx.shardId`) for the calling caller's scope.
|
|
146
|
+
* Attempting to revoke another shard's key or a key in a different
|
|
147
|
+
* tenant returns `false` without raising.
|
|
148
|
+
*
|
|
149
|
+
* Successful revocations fire `onKeyRevoked` on the client side via
|
|
150
|
+
* the existing SSE channel — same path the browser-driven DELETE
|
|
151
|
+
* already takes.
|
|
152
|
+
*/
|
|
153
|
+
revokeKey(c: unknown, id: string): Promise<boolean>;
|
|
98
154
|
/**
|
|
99
155
|
* Translate an SH3 document path to a real filesystem path on the host.
|
|
100
156
|
*
|
package/dist/sh3Api/headless.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { listRegisteredApps, getActiveApp } from '../apps/registry.svelte';
|
|
17
17
|
import { launchApp } from '../apps/lifecycle';
|
|
18
|
-
import { registeredShards, listStandaloneViews } from '../shards/
|
|
18
|
+
import { registeredShards, listStandaloneViews } from '../shards/lifecycle.svelte';
|
|
19
19
|
import { inspectActiveLayout, focusView, closeTab, popoutView, dockFloat, dockIntoActiveLayout, locateSlot as locateSlotInActiveLayout, } from '../layout/inspection';
|
|
20
20
|
import { floatManager } from '../overlays/float';
|
|
21
21
|
import { getUser, isAdmin } from '../auth/index';
|
|
@@ -65,7 +65,7 @@ export const sh3coreShard = {
|
|
|
65
65
|
{ id: 'sh3:keys-and-peers', label: 'Keys & Peers' },
|
|
66
66
|
],
|
|
67
67
|
},
|
|
68
|
-
|
|
68
|
+
register(ctx) {
|
|
69
69
|
const zones = ctx.state({
|
|
70
70
|
user: {
|
|
71
71
|
bindings: {},
|
|
@@ -165,9 +165,4 @@ export const sh3coreShard = {
|
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
},
|
|
168
|
-
autostart() {
|
|
169
|
-
// Intentionally empty. Defining this field is what puts the sh3core
|
|
170
|
-
// pseudo-shard on the self-starting path at boot (see bootstrap),
|
|
171
|
-
// so `sh3core:home` is available before any app launches.
|
|
172
|
-
},
|
|
173
168
|
};
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
3
|
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
4
|
import { __setEnvServerUrl } from '../env/index';
|
|
5
|
-
import { registerShard, activateShard, __resetShardRegistryForTest, } from './
|
|
5
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from './lifecycle.svelte';
|
|
6
6
|
import { __resetViewRegistryForTest } from './registry';
|
|
7
7
|
describe('ctx.fetch', () => {
|
|
8
8
|
let originalFetch;
|
|
@@ -27,7 +27,7 @@ describe('ctx.fetch', () => {
|
|
|
27
27
|
let captured = null;
|
|
28
28
|
registerShard({
|
|
29
29
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
30
|
-
|
|
30
|
+
register(ctx) { captured = ctx; },
|
|
31
31
|
});
|
|
32
32
|
await activateShard('test');
|
|
33
33
|
await captured.fetch('/api/foo');
|
|
@@ -42,7 +42,7 @@ describe('ctx.fetch', () => {
|
|
|
42
42
|
let captured = null;
|
|
43
43
|
registerShard({
|
|
44
44
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
45
|
-
|
|
45
|
+
register(ctx) { captured = ctx; },
|
|
46
46
|
});
|
|
47
47
|
await activateShard('test');
|
|
48
48
|
await captured.fetch('https://other.example.com/api/bar');
|
|
@@ -57,7 +57,7 @@ describe('ctx.fetch', () => {
|
|
|
57
57
|
let captured = null;
|
|
58
58
|
registerShard({
|
|
59
59
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
60
|
-
|
|
60
|
+
register(ctx) { captured = ctx; },
|
|
61
61
|
});
|
|
62
62
|
await activateShard('test');
|
|
63
63
|
await captured.fetch('api/baz');
|
|
@@ -79,7 +79,7 @@ describe('ctx.serverUrl', () => {
|
|
|
79
79
|
let captured = null;
|
|
80
80
|
registerShard({
|
|
81
81
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
82
|
-
|
|
82
|
+
register(ctx) { captured = ctx; },
|
|
83
83
|
});
|
|
84
84
|
await activateShard('test');
|
|
85
85
|
expect(captured.serverUrl).toBe('https://example.com');
|
|
@@ -89,7 +89,7 @@ describe('ctx.serverUrl', () => {
|
|
|
89
89
|
let captured = null;
|
|
90
90
|
registerShard({
|
|
91
91
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
92
|
-
|
|
92
|
+
register(ctx) { captured = ctx; },
|
|
93
93
|
});
|
|
94
94
|
await activateShard('test');
|
|
95
95
|
expect(captured.serverUrl).toBe('');
|
|
@@ -110,7 +110,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
110
110
|
let captured = null;
|
|
111
111
|
registerShard({
|
|
112
112
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
113
|
-
|
|
113
|
+
register(ctx) { captured = ctx; },
|
|
114
114
|
});
|
|
115
115
|
await activateShard('test');
|
|
116
116
|
expect(captured.resolveUrl('/api/foo')).toBe('https://example.com/api/foo');
|
|
@@ -119,7 +119,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
119
119
|
let captured = null;
|
|
120
120
|
registerShard({
|
|
121
121
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
122
|
-
|
|
122
|
+
register(ctx) { captured = ctx; },
|
|
123
123
|
});
|
|
124
124
|
await activateShard('test');
|
|
125
125
|
expect(captured.resolveUrl('https://other.example.com/ws')).toBe('https://other.example.com/ws');
|
|
@@ -128,7 +128,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
128
128
|
let captured = null;
|
|
129
129
|
registerShard({
|
|
130
130
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
131
|
-
|
|
131
|
+
register(ctx) { captured = ctx; },
|
|
132
132
|
});
|
|
133
133
|
await activateShard('test');
|
|
134
134
|
expect(captured.resolveUrl('api/ws')).toBe('https://example.com/api/ws');
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { Shard, ShardContext } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Reactive registry of every shard known to the host. Keys are shard ids.
|
|
4
|
+
* Populated by `registerShard`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const registeredShards: Map<string, Shard>;
|
|
7
|
+
/**
|
|
8
|
+
* Reactive map of shard ids that failed during the lifecycle. Populated
|
|
9
|
+
* by registerAllShards and related operations.
|
|
10
|
+
*/
|
|
11
|
+
export interface ShardErrorEntry {
|
|
12
|
+
id: string;
|
|
13
|
+
error: unknown;
|
|
14
|
+
phase: 'register' | 'launch' | 'satellite';
|
|
15
|
+
timestamp: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const erroredShards: Map<string, ShardErrorEntry>;
|
|
18
|
+
/** Read the app id currently bound to this shard, or null. */
|
|
19
|
+
export declare function getShardBinding(shardId: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Update which app's namespace this shard's `ctx.documents` resolves to.
|
|
22
|
+
* Pass `appId` to bind, `null` to unbind. Internal — only the lifecycle
|
|
23
|
+
* module and `apps/lifecycle.ts` call this.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rotateShardDocumentNamespace(shardId: string, appId: string | null): void;
|
|
26
|
+
export interface ShardEntry {
|
|
27
|
+
shard: Shard;
|
|
28
|
+
ctx: ShardContext;
|
|
29
|
+
viewIds: Set<string>;
|
|
30
|
+
verbNames: Set<string>;
|
|
31
|
+
/** Cleanup fns from things registered inside `register()`. Disposed only on shard re-register or framework teardown. */
|
|
32
|
+
bootCleanupFns: (() => Promise<void> | void)[];
|
|
33
|
+
/** Per-(shardId, appId) cleanup bags for things registered inside `onAppActivate`. Keyed by appId. */
|
|
34
|
+
appCleanupBags: Map<string, (() => Promise<void> | void)[]>;
|
|
35
|
+
/** Active appId during `onAppActivate` invocation, so register sites can route disposers to the right bag. */
|
|
36
|
+
activeAppId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export declare function __setScopeResolver(resolver: (() => 'tenant' | 'project') | null): void;
|
|
39
|
+
/**
|
|
40
|
+
* Build a ShardContext for the given shard. The ctx is permanent for the
|
|
41
|
+
* shard's lifetime; the same instance is passed to `register`, every
|
|
42
|
+
* `onAppActivate`/`onAppDeactivate`, and every auxiliary hook.
|
|
43
|
+
*
|
|
44
|
+
* `entry` tracks cleanup bags. When the shard is inside an `onAppActivate`
|
|
45
|
+
* call, register sites route disposers to the per-app bag (keyed by
|
|
46
|
+
* `entry.activeAppId`); otherwise they go to the boot bag.
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildShardContext(shard: Shard, entry: ShardEntry): ShardContext;
|
|
49
|
+
export declare const shardEntries: Map<string, ShardEntry>;
|
|
50
|
+
export declare const activeShards: Map<string, Shard>;
|
|
51
|
+
/**
|
|
52
|
+
* Run `register(ctx)` on every registered shard. Idempotent — calling on
|
|
53
|
+
* an already-entered shard is a no-op. Errors are recorded in
|
|
54
|
+
* `erroredShards` with phase 'register'; one failure does not block others.
|
|
55
|
+
*/
|
|
56
|
+
export declare function registerAllShards(): Promise<void>;
|
|
57
|
+
export declare function runAppActivate(shardId: string, appId: string): Promise<void>;
|
|
58
|
+
export declare function runAppDeactivate(shardId: string, appId: string): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Tear down the active entry for a shard and rebuild it from the current
|
|
61
|
+
* `registeredShards.get(id)` value. Used by `registerShard` when replacing
|
|
62
|
+
* an existing shard with a fresh module (package update, dev hot-reload).
|
|
63
|
+
*
|
|
64
|
+
* Fires `onAppDeactivate` for every bound app, calls `deactivate?.()`,
|
|
65
|
+
* flushes all cleanup bags, builds a fresh ctx, then re-runs `register()`.
|
|
66
|
+
* Caller is responsible for re-invoking `runAppActivate` for any apps
|
|
67
|
+
* currently active that require this shard.
|
|
68
|
+
*/
|
|
69
|
+
export declare function rebuildShardEntry(shardId: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Register (or re-register) a shard. Records the shard in `registeredShards`.
|
|
72
|
+
* If the shard is already active (in `shardEntries`), triggers a hot-swap
|
|
73
|
+
* via `rebuildShardEntry` so the new module replaces the old one cleanly.
|
|
74
|
+
*/
|
|
75
|
+
export declare function registerShard(shard: Shard): void;
|
|
76
|
+
/** True if the shard has been registered AND its register() has run. */
|
|
77
|
+
export declare function isActive(id: string): boolean;
|
|
78
|
+
/** Return the ShardContext for an active shard, or undefined. */
|
|
79
|
+
export declare function getShardContext(id: string): ShardContext | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Enumerate every view declared as `standalone` across the currently
|
|
82
|
+
* registered/active shards.
|
|
83
|
+
*/
|
|
84
|
+
export declare function listStandaloneViews(): Array<{
|
|
85
|
+
shardId: string;
|
|
86
|
+
viewId: string;
|
|
87
|
+
label: string;
|
|
88
|
+
}>;
|
|
89
|
+
/** Test-only reset. */
|
|
90
|
+
export declare function __resetLifecycleForTest(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Test-only reset for the full shard registry. Wipes all live entries,
|
|
93
|
+
* registered shards, and error records.
|
|
94
|
+
*/
|
|
95
|
+
export declare function __resetShardRegistryForTest(): void;
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated v2 compat shim — use `registerAllShards()` to run register
|
|
98
|
+
* for every registered shard. This shim activates a single shard by
|
|
99
|
+
* invoking its register hook directly. Retained for test fixtures during
|
|
100
|
+
* the migration; Phase 7 sweeps callers.
|
|
101
|
+
*/
|
|
102
|
+
export declare function activateShard(id: string): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* @deprecated v2 compat shim — in v3, shards stay alive for the whole
|
|
105
|
+
* session. This shim performs a full teardown of the entry (for tests
|
|
106
|
+
* that explicitly want to verify cleanup paths).
|
|
107
|
+
*/
|
|
108
|
+
export declare function deactivateShard(id: string): void;
|