sh3-core 0.22.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +3 -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/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
package/dist/shards/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StateZones } from '../state/zones.svelte';
|
|
2
2
|
import type { ZoneSchema, ZoneManager } from '../state/types';
|
|
3
|
-
import type { DocumentHandle
|
|
3
|
+
import type { DocumentHandle } from '../documents/types';
|
|
4
4
|
import type { BrowseCapability } from '../documents/browse';
|
|
5
5
|
import type { DocumentPickerApi } from '../documents/picker-api';
|
|
6
6
|
import type { EnvState } from '../env/types';
|
|
@@ -91,21 +91,6 @@ export interface MountContext {
|
|
|
91
91
|
*/
|
|
92
92
|
location(): TreeRootRef | null;
|
|
93
93
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Passed to `Shard.onAppActivate`. The `documents()` factory remains for
|
|
96
|
-
* back-compat (it returns a handle scoped to the app namespace, identical
|
|
97
|
-
* to what `ctx.documents()` now returns by default). Prefer `ctx.documents()`
|
|
98
|
-
* in new code — the framework auto-scopes the shard's existing handle to
|
|
99
|
-
* the active app's namespace via `shardAppBindings`.
|
|
100
|
-
*
|
|
101
|
-
* @deprecated Use `ctx.documents()` — auto-binds to the active app.
|
|
102
|
-
*/
|
|
103
|
-
export interface AppActivateContext {
|
|
104
|
-
/** The id of the app that just became active. */
|
|
105
|
-
readonly appId: string;
|
|
106
|
-
/** @deprecated Use `ctx.documents()`. */
|
|
107
|
-
documents(options?: DocumentHandleOptions): DocumentHandle;
|
|
108
|
-
}
|
|
109
94
|
/**
|
|
110
95
|
* The shard-side adapter that knows how to bring a view to life inside a
|
|
111
96
|
* given HTMLElement. The container is owned by the framework (the slot);
|
|
@@ -239,8 +224,13 @@ export interface ShardContext {
|
|
|
239
224
|
* @param verb - The verb definition (name, summary, run function).
|
|
240
225
|
*/
|
|
241
226
|
registerVerb(verb: Verb): void;
|
|
242
|
-
/**
|
|
243
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Pre-minted document handle for this shard. Format moves per-call via
|
|
229
|
+
* readText/writeText/readJson/writeJson/readBinary/writeBinary. The
|
|
230
|
+
* handle's namespace resolves lazily on every operation — `{shardId}`
|
|
231
|
+
* when no app is bound, `{appId}` while a required-of app is active.
|
|
232
|
+
*/
|
|
233
|
+
documents: DocumentHandle;
|
|
244
234
|
/**
|
|
245
235
|
* Cross-origin-safe HTTP helper. Resolves relative `/api/...` paths
|
|
246
236
|
* against the configured serverUrl. In Tauri, routes through
|
|
@@ -366,18 +356,14 @@ export interface Shard {
|
|
|
366
356
|
/** Static description of this shard's identity and declared contributions. */
|
|
367
357
|
manifest: ShardManifest;
|
|
368
358
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
* @deprecated Use `registerContributions(ctx)` for static registrations
|
|
376
|
-
* and `activate(ctx)` for imperative setup. `autostart` will be removed
|
|
377
|
-
* in a future ADR — see ADR-026. Existing shards using `autostart` are
|
|
378
|
-
* not broken; this is a migration signal only.
|
|
359
|
+
* Runs once at SH3 boot, for every registered shard, unconditionally.
|
|
360
|
+
* The only place to declare state zones, register views, verbs, actions,
|
|
361
|
+
* custom elements, contributions, and reactive effects. Everything
|
|
362
|
+
* registered here lives for the rest of the SH3 session.
|
|
363
|
+
*
|
|
364
|
+
* v3 contract — replaces v2's `activate()` + `autostart()` + `registerContributions()`.
|
|
379
365
|
*/
|
|
380
|
-
|
|
366
|
+
register(ctx: ShardContext): void | Promise<void>;
|
|
381
367
|
/** Optional cleanup hook called when the shard is deactivated. Release timers, subscriptions, and external resources here. */
|
|
382
368
|
deactivate?(): void | Promise<void>;
|
|
383
369
|
/**
|
|
@@ -385,48 +371,29 @@ export interface Shard {
|
|
|
385
371
|
* remains active; its views and state are preserved. Return `false`
|
|
386
372
|
* (sync or async) to cancel the navigation.
|
|
387
373
|
*/
|
|
388
|
-
suspend?(): void | false | Promise<void | false>;
|
|
374
|
+
suspend?(ctx: ShardContext): void | false | Promise<void | false>;
|
|
389
375
|
/**
|
|
390
376
|
* Called when the owning app resumes from Home. Receives the same
|
|
391
377
|
* `ShardContext` that `activate` received.
|
|
392
378
|
*/
|
|
393
379
|
resume?(ctx: ShardContext): void | Promise<void>;
|
|
394
380
|
/** Fires when a key minted by this shard is revoked from any source. */
|
|
395
|
-
onKeyRevoked?(id: string): void | Promise<void>;
|
|
396
|
-
/**
|
|
397
|
-
* Register views, commands, hotkey bindings, verbs, toolbar items, and
|
|
398
|
-
* menu entries — anything that doesn't depend on session state. Called
|
|
399
|
-
* once after `activate()`. Separating static registrations from ceremony
|
|
400
|
-
* allows the framework to re-call this on hot-reload without re-running
|
|
401
|
-
* the full activation lifecycle. All contributions registered here are
|
|
402
|
-
* auto-unregistered when the shard deactivates.
|
|
403
|
-
*
|
|
404
|
-
* Shards that currently register everything inside `activate()` need not
|
|
405
|
-
* move — `activate()` still works. Use `registerContributions()` for new
|
|
406
|
-
* shards or when you want hot-reload-safe contribution registration.
|
|
407
|
-
*/
|
|
408
|
-
registerContributions?(ctx: ShardContext): void;
|
|
381
|
+
onKeyRevoked?(ctx: ShardContext, id: string): void | Promise<void>;
|
|
409
382
|
/**
|
|
410
|
-
* Called
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
* The `appCtx.documents()` factory is retained for back-compat but is
|
|
417
|
-
* deprecated; new code should use `ctx.documents()`.
|
|
383
|
+
* Called when an app that lists this shard in `requiredShards` launches.
|
|
384
|
+
* v3 signature: receives the shard's ctx plus the activating app's id.
|
|
385
|
+
* The shard's `ctx.documents` namespace has already been rotated to the
|
|
386
|
+
* app's namespace before this fires. Disposers from registrations made
|
|
387
|
+
* inside this hook are tracked in a per-app bag and auto-cleared on
|
|
388
|
+
* the matching `onAppDeactivate`.
|
|
418
389
|
*/
|
|
419
|
-
onAppActivate?(
|
|
390
|
+
onAppActivate?(ctx: ShardContext, appId: string): void | Promise<void>;
|
|
420
391
|
/**
|
|
421
|
-
* Called when
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* uses the `suspend`/`resume` hooks instead. The shard remains active; its
|
|
425
|
-
* background services and standalone document handles are still valid.
|
|
426
|
-
* Use this to release app-scoped document handles and revert to standalone
|
|
427
|
-
* state.
|
|
392
|
+
* Called when an app that requires this shard unloads. v3 signature:
|
|
393
|
+
* receives ctx + appId. The shard stays alive; only per-app
|
|
394
|
+
* registrations (those added inside `onAppActivate`) are torn down.
|
|
428
395
|
*/
|
|
429
|
-
onAppDeactivate?(appId: string): void | Promise<void>;
|
|
396
|
+
onAppDeactivate?(ctx: ShardContext, appId: string): void | Promise<void>;
|
|
430
397
|
/**
|
|
431
398
|
* Called BEFORE layout slots begin mounting, after all required shards have
|
|
432
399
|
* activated. `slots` contains every slot in the restored layout tree that
|
|
@@ -434,7 +401,7 @@ export interface Shard {
|
|
|
434
401
|
* slot-specific contributions here (e.g. `EDITOR_DOCUMENT_POINT` keyed by
|
|
435
402
|
* `slotId`) so they are in place when the view factory's `mount()` is called.
|
|
436
403
|
*/
|
|
437
|
-
onLayoutWillRestore?(slots: RestoredSlot[]): void | Promise<void>;
|
|
404
|
+
onLayoutWillRestore?(ctx: ShardContext, slots: RestoredSlot[]): void | Promise<void>;
|
|
438
405
|
/**
|
|
439
406
|
* Called AFTER the layout has been switched to the app's tree and slots
|
|
440
407
|
* have begun mounting. Use for post-mount wiring and reconciliation.
|
|
@@ -442,7 +409,7 @@ export interface Shard {
|
|
|
442
409
|
* this hook fires after the layout render, not after all `mount()` calls
|
|
443
410
|
* have returned.
|
|
444
411
|
*/
|
|
445
|
-
onLayoutRestored?(slots: RestoredSlot[]): void;
|
|
412
|
+
onLayoutRestored?(ctx: ShardContext, slots: RestoredSlot[]): void;
|
|
446
413
|
}
|
|
447
414
|
/**
|
|
448
415
|
* Source-level shape of a shard as written by external package authors.
|
|
@@ -29,7 +29,7 @@ import { getAuthToken } from '../transport/authToken';
|
|
|
29
29
|
export { makeSh3ApiHeadless, makeSh3ApiForTest } from '../sh3Api/headless';
|
|
30
30
|
export const shellShard = {
|
|
31
31
|
manifest,
|
|
32
|
-
|
|
32
|
+
register(ctx) {
|
|
33
33
|
registerV1Verbs(ctx);
|
|
34
34
|
const shell = makeSh3ApiHeadless(ctx.zones);
|
|
35
35
|
// Bind the shell-shard's workspace zone — backs scrollback persistence
|
|
@@ -85,9 +85,6 @@ export const shellShard = {
|
|
|
85
85
|
};
|
|
86
86
|
ctx.registerView('shell:terminal', factory);
|
|
87
87
|
},
|
|
88
|
-
autostart() {
|
|
89
|
-
// Intentionally empty — same pattern as __sh3core__.
|
|
90
|
-
},
|
|
91
88
|
deactivate() {
|
|
92
89
|
__unbindZone();
|
|
93
90
|
},
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export declare const VERSION = "0.22.
|
|
2
|
+
export declare const VERSION = "0.22.1";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export const VERSION = '0.22.
|
|
2
|
+
export const VERSION = '0.22.1';
|
package/package.json
CHANGED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
|
-
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
|
-
import { registerShard, activateShard, __resetShardRegistryForTest } from './activate.svelte';
|
|
5
|
-
import { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from '../documents/types';
|
|
6
|
-
describe('ctx.browse permission gating', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
__resetShardRegistryForTest();
|
|
9
|
-
__setDocumentBackend(new MemoryDocumentBackend());
|
|
10
|
-
__setActiveScope('tenant-a');
|
|
11
|
-
});
|
|
12
|
-
it('is undefined when no documents permission is declared', async () => {
|
|
13
|
-
let captured = null;
|
|
14
|
-
registerShard({
|
|
15
|
-
manifest: { id: 'no-browse', label: 'n', version: '0.0.0', views: [] },
|
|
16
|
-
activate(ctx) { captured = ctx; },
|
|
17
|
-
});
|
|
18
|
-
await activateShard('no-browse');
|
|
19
|
-
expect(captured.browse).toBeUndefined();
|
|
20
|
-
});
|
|
21
|
-
it('exposes metadata methods when documents:browse is declared', async () => {
|
|
22
|
-
var _a, _b, _c;
|
|
23
|
-
let captured = null;
|
|
24
|
-
registerShard({
|
|
25
|
-
manifest: {
|
|
26
|
-
id: 'has-browse', label: 'b', version: '0.0.0', views: [],
|
|
27
|
-
permissions: [PERMISSION_DOCUMENTS_BROWSE],
|
|
28
|
-
},
|
|
29
|
-
activate(ctx) { captured = ctx; },
|
|
30
|
-
});
|
|
31
|
-
await activateShard('has-browse');
|
|
32
|
-
expect(typeof ((_a = captured.browse) === null || _a === void 0 ? void 0 : _a.listDocuments)).toBe('function');
|
|
33
|
-
expect(typeof ((_b = captured.browse) === null || _b === void 0 ? void 0 : _b.watchDocuments)).toBe('function');
|
|
34
|
-
expect(typeof ((_c = captured.browse) === null || _c === void 0 ? void 0 : _c.listShards)).toBe('function');
|
|
35
|
-
});
|
|
36
|
-
it('omits readFrom and writeTo when only documents:browse is declared', async () => {
|
|
37
|
-
var _a, _b;
|
|
38
|
-
let captured = null;
|
|
39
|
-
registerShard({
|
|
40
|
-
manifest: {
|
|
41
|
-
id: 'browse-only', label: 'b', version: '0.0.0', views: [],
|
|
42
|
-
permissions: [PERMISSION_DOCUMENTS_BROWSE],
|
|
43
|
-
},
|
|
44
|
-
activate(ctx) { captured = ctx; },
|
|
45
|
-
});
|
|
46
|
-
await activateShard('browse-only');
|
|
47
|
-
expect((_a = captured.browse) === null || _a === void 0 ? void 0 : _a.readFrom).toBeUndefined();
|
|
48
|
-
expect((_b = captured.browse) === null || _b === void 0 ? void 0 : _b.writeTo).toBeUndefined();
|
|
49
|
-
});
|
|
50
|
-
it('exposes readFrom when documents:browse + documents:read are declared', async () => {
|
|
51
|
-
var _a, _b;
|
|
52
|
-
let captured = null;
|
|
53
|
-
registerShard({
|
|
54
|
-
manifest: {
|
|
55
|
-
id: 'browse-and-read', label: 'br', version: '0.0.0', views: [],
|
|
56
|
-
permissions: [PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ],
|
|
57
|
-
},
|
|
58
|
-
activate(ctx) { captured = ctx; },
|
|
59
|
-
});
|
|
60
|
-
await activateShard('browse-and-read');
|
|
61
|
-
expect(typeof ((_a = captured.browse) === null || _a === void 0 ? void 0 : _a.readFrom)).toBe('function');
|
|
62
|
-
expect((_b = captured.browse) === null || _b === void 0 ? void 0 : _b.writeTo).toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
it('exposes writeTo when documents:browse + documents:write are declared', async () => {
|
|
65
|
-
var _a, _b;
|
|
66
|
-
let captured = null;
|
|
67
|
-
registerShard({
|
|
68
|
-
manifest: {
|
|
69
|
-
id: 'browse-and-write', label: 'bw', version: '0.0.0', views: [],
|
|
70
|
-
permissions: [PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_WRITE],
|
|
71
|
-
},
|
|
72
|
-
activate(ctx) { captured = ctx; },
|
|
73
|
-
});
|
|
74
|
-
await activateShard('browse-and-write');
|
|
75
|
-
expect((_a = captured.browse) === null || _a === void 0 ? void 0 : _a.readFrom).toBeUndefined();
|
|
76
|
-
expect(typeof ((_b = captured.browse) === null || _b === void 0 ? void 0 : _b.writeTo)).toBe('function');
|
|
77
|
-
});
|
|
78
|
-
it('exposes both readFrom and writeTo when all three permissions are declared', async () => {
|
|
79
|
-
var _a, _b;
|
|
80
|
-
let captured = null;
|
|
81
|
-
registerShard({
|
|
82
|
-
manifest: {
|
|
83
|
-
id: 'full-access', label: 'full', version: '0.0.0', views: [],
|
|
84
|
-
permissions: [
|
|
85
|
-
PERMISSION_DOCUMENTS_BROWSE,
|
|
86
|
-
PERMISSION_DOCUMENTS_READ,
|
|
87
|
-
PERMISSION_DOCUMENTS_WRITE,
|
|
88
|
-
],
|
|
89
|
-
},
|
|
90
|
-
activate(ctx) { captured = ctx; },
|
|
91
|
-
});
|
|
92
|
-
await activateShard('full-access');
|
|
93
|
-
expect(typeof ((_a = captured.browse) === null || _a === void 0 ? void 0 : _a.readFrom)).toBe('function');
|
|
94
|
-
expect(typeof ((_b = captured.browse) === null || _b === void 0 ? void 0 : _b.writeTo)).toBe('function');
|
|
95
|
-
});
|
|
96
|
-
it('yields no ctx.browse when documents:read is declared without documents:browse', async () => {
|
|
97
|
-
let captured = null;
|
|
98
|
-
registerShard({
|
|
99
|
-
manifest: {
|
|
100
|
-
id: 'read-only', label: 'r', version: '0.0.0', views: [],
|
|
101
|
-
permissions: [PERMISSION_DOCUMENTS_READ],
|
|
102
|
-
},
|
|
103
|
-
activate(ctx) { captured = ctx; },
|
|
104
|
-
});
|
|
105
|
-
await activateShard('read-only');
|
|
106
|
-
expect(captured.browse).toBeUndefined();
|
|
107
|
-
});
|
|
108
|
-
it('yields no ctx.browse when documents:write is declared without documents:browse', async () => {
|
|
109
|
-
let captured = null;
|
|
110
|
-
registerShard({
|
|
111
|
-
manifest: {
|
|
112
|
-
id: 'write-only', label: 'w', version: '0.0.0', views: [],
|
|
113
|
-
permissions: [PERMISSION_DOCUMENTS_WRITE],
|
|
114
|
-
},
|
|
115
|
-
activate(ctx) { captured = ctx; },
|
|
116
|
-
});
|
|
117
|
-
await activateShard('write-only');
|
|
118
|
-
expect(captured.browse).toBeUndefined();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
|
-
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
|
-
import { registerShard, activateShard, deactivateShard, __resetShardRegistryForTest, } from './activate.svelte';
|
|
5
|
-
import { __resetContributionsForTest, list, listPoints } from '../contributions';
|
|
6
|
-
describe('ctx.contributions', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
__resetShardRegistryForTest();
|
|
9
|
-
__resetContributionsForTest();
|
|
10
|
-
__setDocumentBackend(new MemoryDocumentBackend());
|
|
11
|
-
__setActiveScope('tenant-a');
|
|
12
|
-
});
|
|
13
|
-
it('is always present on ShardContext (no permission required)', async () => {
|
|
14
|
-
let captured = null;
|
|
15
|
-
registerShard({
|
|
16
|
-
manifest: { id: 'a', label: 'A', version: '0.0.0', views: [] },
|
|
17
|
-
activate(ctx) { captured = ctx; },
|
|
18
|
-
});
|
|
19
|
-
await activateShard('a');
|
|
20
|
-
expect(typeof captured.contributions.register).toBe('function');
|
|
21
|
-
expect(typeof captured.contributions.list).toBe('function');
|
|
22
|
-
expect(typeof captured.contributions.listPoints).toBe('function');
|
|
23
|
-
expect(typeof captured.contributions.onChange).toBe('function');
|
|
24
|
-
});
|
|
25
|
-
it('register() makes the descriptor visible via the global list()', async () => {
|
|
26
|
-
registerShard({
|
|
27
|
-
manifest: { id: 'provider', label: 'P', version: '0.0.0', views: [] },
|
|
28
|
-
activate(ctx) {
|
|
29
|
-
ctx.contributions.register('my.point', { id: 'alpha' });
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
await activateShard('provider');
|
|
33
|
-
expect(list('my.point')).toEqual([{ id: 'alpha' }]);
|
|
34
|
-
});
|
|
35
|
-
it('deactivate auto-unregisters every descriptor the shard registered', async () => {
|
|
36
|
-
registerShard({
|
|
37
|
-
manifest: { id: 'provider', label: 'P', version: '0.0.0', views: [] },
|
|
38
|
-
activate(ctx) {
|
|
39
|
-
ctx.contributions.register('p1', { id: 'a' });
|
|
40
|
-
ctx.contributions.register('p1', { id: 'b' });
|
|
41
|
-
ctx.contributions.register('p2', { id: 'c' });
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
await activateShard('provider');
|
|
45
|
-
expect(list('p1')).toHaveLength(2);
|
|
46
|
-
expect(list('p2')).toHaveLength(1);
|
|
47
|
-
deactivateShard('provider');
|
|
48
|
-
expect(list('p1')).toEqual([]);
|
|
49
|
-
expect(list('p2')).toEqual([]);
|
|
50
|
-
expect(listPoints()).toEqual([]);
|
|
51
|
-
});
|
|
52
|
-
it('deactivate auto-unsubscribes every onChange listener the shard registered', async () => {
|
|
53
|
-
const cb = vi.fn();
|
|
54
|
-
registerShard({
|
|
55
|
-
manifest: { id: 'watcher', label: 'W', version: '0.0.0', views: [] },
|
|
56
|
-
activate(ctx) {
|
|
57
|
-
ctx.contributions.onChange('p', cb);
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
await activateShard('watcher');
|
|
61
|
-
registerShard({
|
|
62
|
-
manifest: { id: 'provider', label: 'P', version: '0.0.0', views: [] },
|
|
63
|
-
activate(ctx) {
|
|
64
|
-
ctx.contributions.register('p', { id: 'x' });
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
await activateShard('provider');
|
|
68
|
-
expect(cb).toHaveBeenCalledTimes(1);
|
|
69
|
-
deactivateShard('watcher');
|
|
70
|
-
// Further registrations must not reach the watcher's callback.
|
|
71
|
-
registerShard({
|
|
72
|
-
manifest: { id: 'provider-2', label: 'P2', version: '0.0.0', views: [] },
|
|
73
|
-
activate(ctx) {
|
|
74
|
-
ctx.contributions.register('p', { id: 'y' });
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
await activateShard('provider-2');
|
|
78
|
-
expect(cb).toHaveBeenCalledTimes(1);
|
|
79
|
-
});
|
|
80
|
-
it('two shards can contribute to the same point independently', async () => {
|
|
81
|
-
registerShard({
|
|
82
|
-
manifest: { id: 'a', label: 'A', version: '0.0.0', views: [] },
|
|
83
|
-
activate(ctx) { ctx.contributions.register('shared', { from: 'a' }); },
|
|
84
|
-
});
|
|
85
|
-
registerShard({
|
|
86
|
-
manifest: { id: 'b', label: 'B', version: '0.0.0', views: [] },
|
|
87
|
-
activate(ctx) { ctx.contributions.register('shared', { from: 'b' }); },
|
|
88
|
-
});
|
|
89
|
-
await activateShard('a');
|
|
90
|
-
await activateShard('b');
|
|
91
|
-
expect(list('shared')).toEqual([{ from: 'a' }, { from: 'b' }]);
|
|
92
|
-
deactivateShard('a');
|
|
93
|
-
expect(list('shared')).toEqual([{ from: 'b' }]);
|
|
94
|
-
});
|
|
95
|
-
it('explicit unregister returned by register() is safe alongside auto-cleanup', async () => {
|
|
96
|
-
let earlyUnregister;
|
|
97
|
-
registerShard({
|
|
98
|
-
manifest: { id: 'p', label: 'P', version: '0.0.0', views: [] },
|
|
99
|
-
activate(ctx) {
|
|
100
|
-
earlyUnregister = ctx.contributions.register('p', { id: 'a' });
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
await activateShard('p');
|
|
104
|
-
expect(list('p')).toHaveLength(1);
|
|
105
|
-
earlyUnregister();
|
|
106
|
-
expect(list('p')).toEqual([]);
|
|
107
|
-
// Deactivate must not throw when the entry is already gone.
|
|
108
|
-
expect(() => deactivateShard('p')).not.toThrow();
|
|
109
|
-
});
|
|
110
|
-
it('onAnyChange fires for register/unregister at any point and auto-unsubscribes on deactivate', async () => {
|
|
111
|
-
const cb = vi.fn();
|
|
112
|
-
registerShard({
|
|
113
|
-
manifest: { id: 'watcher', label: 'W', version: '0.0.0', views: [] },
|
|
114
|
-
activate(ctx) {
|
|
115
|
-
ctx.contributions.onAnyChange(cb);
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
await activateShard('watcher');
|
|
119
|
-
registerShard({
|
|
120
|
-
manifest: { id: 'p1', label: 'P1', version: '0.0.0', views: [] },
|
|
121
|
-
activate(ctx) {
|
|
122
|
-
ctx.contributions.register('point.a', { id: 'x' });
|
|
123
|
-
ctx.contributions.register('point.b', { id: 'y' });
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
await activateShard('p1');
|
|
127
|
-
expect(cb).toHaveBeenCalledTimes(2);
|
|
128
|
-
expect(cb).toHaveBeenCalledWith('point.a');
|
|
129
|
-
expect(cb).toHaveBeenCalledWith('point.b');
|
|
130
|
-
deactivateShard('watcher');
|
|
131
|
-
cb.mockClear();
|
|
132
|
-
registerShard({
|
|
133
|
-
manifest: { id: 'p2', label: 'P2', version: '0.0.0', views: [] },
|
|
134
|
-
activate(ctx) {
|
|
135
|
-
ctx.contributions.register('point.c', { id: 'z' });
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
await activateShard('p2');
|
|
139
|
-
expect(cb).not.toHaveBeenCalled();
|
|
140
|
-
});
|
|
141
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
|
-
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
|
-
import { registerShard, activateShard, registeredShards, activeShards, __resetShardRegistryForTest, erroredShards, } from './activate.svelte';
|
|
5
|
-
describe('erroredShards map', () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
__resetShardRegistryForTest();
|
|
8
|
-
__setDocumentBackend(new MemoryDocumentBackend());
|
|
9
|
-
__setActiveScope('tenant-a');
|
|
10
|
-
});
|
|
11
|
-
it('is empty after reset', () => {
|
|
12
|
-
expect(erroredShards.size).toBe(0);
|
|
13
|
-
});
|
|
14
|
-
it('supports the Map read API used by callers', () => {
|
|
15
|
-
expect(typeof erroredShards.has).toBe('function');
|
|
16
|
-
expect(typeof erroredShards.get).toBe('function');
|
|
17
|
-
expect(erroredShards.has('anything')).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
describe('activateShard — unwind on activation failure', () => {
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
__resetShardRegistryForTest();
|
|
23
|
-
__setDocumentBackend(new MemoryDocumentBackend());
|
|
24
|
-
__setActiveScope('tenant-a');
|
|
25
|
-
});
|
|
26
|
-
it('unwinds partial state and records the error when activate throws', async () => {
|
|
27
|
-
const shard = {
|
|
28
|
-
manifest: {
|
|
29
|
-
id: 'broken',
|
|
30
|
-
label: 'Broken',
|
|
31
|
-
version: '0.0.0',
|
|
32
|
-
views: [],
|
|
33
|
-
},
|
|
34
|
-
activate(ctx) {
|
|
35
|
-
ctx.registerView('broken:view', { mount: () => ({ unmount() { } }) });
|
|
36
|
-
throw new Error('dependency missing');
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
registerShard(shard);
|
|
40
|
-
await expect(activateShard('broken')).rejects.toThrow('dependency missing');
|
|
41
|
-
expect(activeShards.has('broken')).toBe(false);
|
|
42
|
-
expect(registeredShards.has('broken')).toBe(true);
|
|
43
|
-
const entry = erroredShards.get('broken');
|
|
44
|
-
expect(entry).toBeDefined();
|
|
45
|
-
expect(entry === null || entry === void 0 ? void 0 : entry.id).toBe('broken');
|
|
46
|
-
expect(entry === null || entry === void 0 ? void 0 : entry.phase).toBe('launch');
|
|
47
|
-
expect(entry === null || entry === void 0 ? void 0 : entry.error).toBeInstanceOf(Error);
|
|
48
|
-
expect(typeof (entry === null || entry === void 0 ? void 0 : entry.timestamp)).toBe('number');
|
|
49
|
-
const { getView } = await import('./registry');
|
|
50
|
-
expect(getView('broken:view')).toBeUndefined();
|
|
51
|
-
});
|
|
52
|
-
it('records phase "autostart" when called with that option', async () => {
|
|
53
|
-
var _a;
|
|
54
|
-
const shard = {
|
|
55
|
-
manifest: { id: 'broken-auto', label: 'B', version: '0.0.0', views: [] },
|
|
56
|
-
activate() {
|
|
57
|
-
throw new Error('no');
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
registerShard(shard);
|
|
61
|
-
await expect(activateShard('broken-auto', { phase: 'autostart' })).rejects.toThrow('no');
|
|
62
|
-
expect((_a = erroredShards.get('broken-auto')) === null || _a === void 0 ? void 0 : _a.phase).toBe('autostart');
|
|
63
|
-
});
|
|
64
|
-
it('clears the error entry when the shard is re-registered', async () => {
|
|
65
|
-
const broken = {
|
|
66
|
-
manifest: { id: 'reborn', label: 'R', version: '0.0.0', views: [] },
|
|
67
|
-
activate() {
|
|
68
|
-
throw new Error('first try');
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
registerShard(broken);
|
|
72
|
-
await expect(activateShard('reborn')).rejects.toThrow('first try');
|
|
73
|
-
expect(erroredShards.has('reborn')).toBe(true);
|
|
74
|
-
const fixed = {
|
|
75
|
-
manifest: { id: 'reborn', label: 'R', version: '0.0.1', views: [] },
|
|
76
|
-
activate() { },
|
|
77
|
-
};
|
|
78
|
-
registerShard(fixed);
|
|
79
|
-
expect(erroredShards.has('reborn')).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
it('clears the error entry when activation eventually succeeds', async () => {
|
|
82
|
-
let shouldFail = true;
|
|
83
|
-
const shard = {
|
|
84
|
-
manifest: { id: 'flaky', label: 'F', version: '0.0.0', views: [] },
|
|
85
|
-
activate() {
|
|
86
|
-
if (shouldFail)
|
|
87
|
-
throw new Error('first try');
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
registerShard(shard);
|
|
91
|
-
await expect(activateShard('flaky')).rejects.toThrow('first try');
|
|
92
|
-
expect(erroredShards.has('flaky')).toBe(true);
|
|
93
|
-
shouldFail = false;
|
|
94
|
-
await activateShard('flaky');
|
|
95
|
-
expect(erroredShards.has('flaky')).toBe(false);
|
|
96
|
-
expect(activeShards.has('flaky')).toBe(true);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|