sh3-core 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/BrandSlot.svelte +2 -2
  2. package/dist/actions/ctx-actions.svelte.test.js +2 -2
  3. package/dist/api.d.ts +2 -2
  4. package/dist/api.js +1 -1
  5. package/dist/app/store/StoreView.svelte +26 -35
  6. package/dist/app/store/storeShard.svelte.js +35 -49
  7. package/dist/app/store/verbs.js +24 -55
  8. package/dist/artifact.d.ts +2 -0
  9. package/dist/boot/satellitePayload.d.ts +2 -0
  10. package/dist/boot/satellitePayload.test.js +19 -0
  11. package/dist/build.d.ts +7 -1
  12. package/dist/build.js +34 -9
  13. package/dist/build.test.js +27 -1
  14. package/dist/createShell.js +34 -9
  15. package/dist/documents/browse.d.ts +20 -0
  16. package/dist/documents/browse.js +35 -0
  17. package/dist/documents/browse.test.js +125 -0
  18. package/dist/documents/config.d.ts +0 -4
  19. package/dist/documents/config.js +0 -8
  20. package/dist/documents/http-backend.d.ts +5 -0
  21. package/dist/documents/http-backend.js +25 -0
  22. package/dist/documents/http-backend.test.js +66 -0
  23. package/dist/documents/index.d.ts +1 -1
  24. package/dist/documents/index.js +1 -1
  25. package/dist/documents/types.d.ts +11 -0
  26. package/dist/env/client.d.ts +6 -10
  27. package/dist/env/client.js +11 -21
  28. package/dist/env/index.d.ts +2 -1
  29. package/dist/env/index.js +1 -1
  30. package/dist/host-entry.d.ts +1 -1
  31. package/dist/host-entry.js +1 -1
  32. package/dist/host.d.ts +1 -1
  33. package/dist/host.js +1 -1
  34. package/dist/layout/slotHostPool.svelte.js +2 -2
  35. package/dist/overlays/FloatFrame.svelte +1 -0
  36. package/dist/projects/session-state.svelte.d.ts +3 -0
  37. package/dist/projects/session-state.svelte.js +25 -0
  38. package/dist/projects/session-state.test.js +43 -2
  39. package/dist/projects-shard/ProjectsSection.svelte +14 -18
  40. package/dist/registry/archive.d.ts +12 -0
  41. package/dist/registry/archive.js +80 -0
  42. package/dist/registry/archive.test.d.ts +1 -0
  43. package/dist/registry/archive.test.js +84 -0
  44. package/dist/registry/client.d.ts +9 -29
  45. package/dist/registry/client.js +14 -60
  46. package/dist/registry/client.test.js +31 -21
  47. package/dist/registry/index.d.ts +2 -2
  48. package/dist/registry/index.js +1 -1
  49. package/dist/registry/installer.d.ts +4 -4
  50. package/dist/registry/installer.js +74 -45
  51. package/dist/registry/schema.js +4 -27
  52. package/dist/registry/schema.test.d.ts +1 -0
  53. package/dist/registry/schema.test.js +41 -0
  54. package/dist/registry/types.d.ts +16 -41
  55. package/dist/runtime/runVerb-shell.test.js +2 -2
  56. package/dist/runtime/runVerb.test.js +2 -2
  57. package/dist/sh3core-shard/appActions.js +5 -2
  58. package/dist/shards/activate-browse.test.js +2 -2
  59. package/dist/shards/activate-contributions.test.js +2 -2
  60. package/dist/shards/activate-error-isolation.test.js +3 -3
  61. package/dist/shards/activate-on-key-revoked.test.js +2 -2
  62. package/dist/shards/activate-runtime.test.js +2 -2
  63. package/dist/shards/activate.svelte.js +4 -4
  64. package/dist/shards/ctx-fetch.test.js +4 -4
  65. package/dist/shell-shard/verbs/xfer.js +13 -27
  66. package/dist/shell-shard/verbs/xfer.test.js +36 -25
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/package.json +3 -2
@@ -16,11 +16,11 @@
16
16
  */
17
17
  import { loadBundleModule } from './loader';
18
18
  import { savePackage, loadBundle, listInstalled, removePackage } from './storage';
19
- import { verifyIntegrity } from './integrity';
20
19
  import { deactivateShard } from '../shards/activate.svelte';
21
20
  import { unregisterApp } from '../apps/lifecycle';
22
21
  import { registerLoadedBundle } from './register';
23
22
  import { extractBundlePermissions } from './permission-descriptions';
23
+ import { fetchServerPackages } from '../env/client';
24
24
  /**
25
25
  * Install a package from raw bundle bytes and metadata.
26
26
  *
@@ -35,25 +35,8 @@ import { extractBundlePermissions } from './permission-descriptions';
35
35
  * @returns Result object indicating success/failure and hot-load status.
36
36
  */
37
37
  export async function installPackage(bundle, meta, options) {
38
- // 1. Verify bundle integrity before executing any code.
39
- if (!meta.integrity) {
40
- return {
41
- success: false,
42
- hotLoaded: false,
43
- error: 'Missing integrity hash — refusing to install unverified bundle',
44
- };
45
- }
46
- try {
47
- await verifyIntegrity(bundle, meta.integrity);
48
- }
49
- catch (err) {
50
- return {
51
- success: false,
52
- hotLoaded: false,
53
- error: `Integrity check failed: ${err instanceof Error ? err.message : String(err)}`,
54
- };
55
- }
56
- // 2. Load the module from verified bytes (or reuse the caller's copy).
38
+ // 1. Load the module from bytes (or reuse the caller's copy).
39
+ // Archive integrity is verified upstream in fetchArchive() before extraction.
57
40
  let loaded;
58
41
  if (options === null || options === void 0 ? void 0 : options.loaded) {
59
42
  loaded = options.loaded;
@@ -160,40 +143,86 @@ export async function listInstalledPackages() {
160
143
  return listInstalled();
161
144
  }
162
145
  /**
163
- * Load all installed packages from IndexedDB and register them.
146
+ * Sync installed packages against the server list and load all into the framework.
164
147
  *
165
- * Called once at boot by `bootstrap()`, before any glob-discovered shards
166
- * or the sh3 shard are registered. Individual package failures are logged
167
- * as warnings but do not prevent the sh3 from booting.
148
+ * Server list is authoritative. Packages on server but missing from IndexedDB
149
+ * are fetched from the server's /packages/:id/client.js endpoint and cached.
150
+ * Packages in IndexedDB but absent from the server are evicted.
168
151
  */
169
152
  export async function loadInstalledPackages() {
170
- let packages;
153
+ let serverPackages = [];
171
154
  try {
172
- packages = await listInstalled();
155
+ serverPackages = await fetchServerPackages();
173
156
  }
174
157
  catch (err) {
175
- console.warn('[sh3] Failed to read installed packages from storage:', err instanceof Error ? err.message : err);
158
+ console.warn('[sh3] Could not reach server for package sync, loading from local cache:', err instanceof Error ? err.message : err);
159
+ const fallback = await listInstalled().catch(() => []);
160
+ for (const pkg of fallback) {
161
+ await _loadFromIndexedDB(pkg);
162
+ }
176
163
  return;
177
164
  }
178
- for (const pkg of packages) {
179
- try {
180
- const bytes = await loadBundle(pkg.id);
181
- if (!bytes) {
182
- console.warn(`[sh3] No bundle found for installed package "${pkg.id}", skipping`);
183
- continue;
184
- }
185
- const loaded = await loadBundleModule(bytes);
186
- registerLoadedBundle(loaded, {
187
- version: pkg.version,
188
- sourceRegistry: pkg.sourceRegistry,
189
- contractVersion: pkg.contractVersion,
190
- });
191
- if (loaded.shards.length === 0 && loaded.apps.length === 0) {
192
- console.warn(`[sh3] Package "${pkg.id}" contains no valid shards or apps, skipping`);
193
- }
165
+ const serverIds = new Set(serverPackages.map(p => p.id));
166
+ let localPackages = [];
167
+ try {
168
+ localPackages = await listInstalled();
169
+ }
170
+ catch ( /* treat as empty */_a) { /* treat as empty */ }
171
+ const localIds = new Set(localPackages.map(p => p.id));
172
+ // Evict packages no longer on the server
173
+ for (const pkg of localPackages) {
174
+ if (!serverIds.has(pkg.id)) {
175
+ await removePackage(pkg.id).catch(() => { });
194
176
  }
195
- catch (err) {
196
- console.warn(`[sh3] Failed to load installed package "${pkg.id}":`, err instanceof Error ? err.message : err);
177
+ }
178
+ // Load packages from server use IndexedDB cache if available, else fetch from server
179
+ for (const serverPkg of serverPackages) {
180
+ if (localIds.has(serverPkg.id)) {
181
+ const localPkg = localPackages.find(p => p.id === serverPkg.id);
182
+ await _loadFromIndexedDB(localPkg);
183
+ }
184
+ else {
185
+ await _fetchAndCacheFromServer(serverPkg);
186
+ }
187
+ }
188
+ }
189
+ async function _loadFromIndexedDB(pkg) {
190
+ try {
191
+ const bytes = await loadBundle(pkg.id);
192
+ if (!bytes) {
193
+ console.warn(`[sh3] No bundle in IndexedDB for "${pkg.id}", skipping`);
194
+ return;
195
+ }
196
+ const loaded = await loadBundleModule(bytes);
197
+ registerLoadedBundle(loaded, { version: pkg.version, sourceRegistry: pkg.sourceRegistry, contractVersion: pkg.contractVersion });
198
+ }
199
+ catch (err) {
200
+ console.warn(`[sh3] Failed to load "${pkg.id}" from cache:`, err instanceof Error ? err.message : err);
201
+ }
202
+ }
203
+ async function _fetchAndCacheFromServer(serverPkg) {
204
+ var _a, _b;
205
+ try {
206
+ const res = await fetch(serverPkg.bundleUrl);
207
+ if (!res.ok) {
208
+ console.warn(`[sh3] Failed to fetch bundle for "${serverPkg.id}": HTTP ${res.status}`);
209
+ return;
197
210
  }
211
+ const bundle = await res.arrayBuffer();
212
+ const record = {
213
+ id: serverPkg.id,
214
+ type: serverPkg.type,
215
+ version: serverPkg.version,
216
+ sourceRegistry: (_a = serverPkg.sourceRegistry) !== null && _a !== void 0 ? _a : '',
217
+ contractVersion: (_b = serverPkg.contractVersion) !== null && _b !== void 0 ? _b : '',
218
+ installedAt: new Date().toISOString(),
219
+ permissions: [],
220
+ };
221
+ await savePackage(serverPkg.id, bundle, record);
222
+ const loaded = await loadBundleModule(bundle);
223
+ registerLoadedBundle(loaded, { version: record.version, sourceRegistry: record.sourceRegistry, contractVersion: record.contractVersion });
224
+ }
225
+ catch (err) {
226
+ console.warn(`[sh3] Failed to fetch/cache "${serverPkg.id}" from server:`, err instanceof Error ? err.message : err);
198
227
  }
199
228
  }
@@ -113,29 +113,8 @@ function validatePackageVersion(data, path) {
113
113
  const obj = data;
114
114
  requireString(obj, 'version', path);
115
115
  requireString(obj, 'contractVersion', path);
116
- // Client bundle fields — optional individually, but if either is present both must be.
117
- const hasBundleUrl = 'bundleUrl' in obj && obj.bundleUrl !== undefined;
118
- const hasIntegrity = 'integrity' in obj && obj.integrity !== undefined;
119
- if (hasBundleUrl)
120
- requireString(obj, 'bundleUrl', path);
121
- if (hasIntegrity)
122
- requireString(obj, 'integrity', path);
123
- if (hasBundleUrl !== hasIntegrity) {
124
- throw new RegistryValidationError(path, 'bundleUrl and integrity must be provided together');
125
- }
126
- // Optional server bundle URL.
127
- const hasServerBundleUrl = 'serverBundleUrl' in obj && obj.serverBundleUrl !== undefined;
128
- if (hasServerBundleUrl) {
129
- requireString(obj, 'serverBundleUrl', path);
130
- }
131
- // Optional server bundle integrity hash — provisional, see ADR-015.
132
- if ('serverIntegrity' in obj && obj.serverIntegrity !== undefined) {
133
- requireString(obj, 'serverIntegrity', path);
134
- }
135
- // A version must ship at least one bundle.
136
- if (!hasBundleUrl && !hasServerBundleUrl) {
137
- throw new RegistryValidationError(path, 'expected at least one of bundleUrl+integrity or serverBundleUrl');
138
- }
116
+ requireString(obj, 'archiveUrl', path);
117
+ requireString(obj, 'integrity', path);
139
118
  let requires;
140
119
  if (obj['requires'] !== undefined) {
141
120
  if (!Array.isArray(obj['requires'])) {
@@ -146,10 +125,8 @@ function validatePackageVersion(data, path) {
146
125
  return {
147
126
  version: obj['version'],
148
127
  contractVersion: obj['contractVersion'],
149
- bundleUrl: typeof obj['bundleUrl'] === 'string' ? obj['bundleUrl'] : undefined,
150
- integrity: typeof obj['integrity'] === 'string' ? obj['integrity'] : undefined,
151
- serverBundleUrl: typeof obj['serverBundleUrl'] === 'string' ? obj['serverBundleUrl'] : undefined,
152
- serverIntegrity: typeof obj['serverIntegrity'] === 'string' ? obj['serverIntegrity'] : undefined,
128
+ archiveUrl: obj['archiveUrl'],
129
+ integrity: obj['integrity'],
153
130
  requires,
154
131
  };
155
132
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateRegistryIndex, RegistryValidationError } from './schema.js';
3
+ const VALID_VERSION = {
4
+ version: '1.0.0',
5
+ contractVersion: '1',
6
+ archiveUrl: 'https://example.com/pkg-1.0.0.sh3pkg',
7
+ integrity: 'sha384-abc123',
8
+ };
9
+ const VALID_ENTRY = {
10
+ id: 'my-shard',
11
+ type: 'shard',
12
+ label: 'My Shard',
13
+ description: 'A test shard',
14
+ author: { name: 'Test Author' },
15
+ versions: [VALID_VERSION],
16
+ };
17
+ const VALID_INDEX = { version: 1, packages: [VALID_ENTRY] };
18
+ describe('validateRegistryIndex', () => {
19
+ it('accepts a valid index with archiveUrl and integrity', () => {
20
+ const result = validateRegistryIndex(VALID_INDEX);
21
+ expect(result.packages[0].versions[0].archiveUrl).toBe('https://example.com/pkg-1.0.0.sh3pkg');
22
+ expect(result.packages[0].versions[0].integrity).toBe('sha384-abc123');
23
+ });
24
+ it('rejects a version missing archiveUrl', () => {
25
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [{ version: '1.0.0', contractVersion: '1', integrity: 'sha384-abc' }] })] });
26
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
27
+ });
28
+ it('rejects a version missing integrity', () => {
29
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [{ version: '1.0.0', contractVersion: '1', archiveUrl: 'https://x.com/a.sh3pkg' }] })] });
30
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
31
+ });
32
+ it('accepts optional requires array', () => {
33
+ const withRequires = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [Object.assign(Object.assign({}, VALID_VERSION), { requires: [{ id: 'dep-shard', versionRange: '^1.0.0' }] })] })] });
34
+ const result = validateRegistryIndex(withRequires);
35
+ expect(result.packages[0].versions[0].requires[0].id).toBe('dep-shard');
36
+ });
37
+ it('rejects a version with requires that has an invalid entry', () => {
38
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [Object.assign(Object.assign({}, VALID_VERSION), { requires: [{ id: 'dep' }] })] })] });
39
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
40
+ });
41
+ });
@@ -81,9 +81,9 @@ export interface PackageEntry {
81
81
  /**
82
82
  * A specific published version of a package.
83
83
  *
84
- * Each version ships a self-contained pre-built ESM bundle at `bundleUrl`.
85
- * The `integrity` field is an SRI hash (sha384 recommended) used to verify
86
- * the download before execution.
84
+ * Each version ships a `.sh3pkg` ZIP archive at `archiveUrl`.
85
+ * The archive contains `manifest.json` and at least one of `client.js` / `server.js`.
86
+ * The `integrity` SRI hash verifies the download before execution.
87
87
  */
88
88
  export interface PackageVersion {
89
89
  /**
@@ -98,36 +98,17 @@ export interface PackageVersion {
98
98
  */
99
99
  contractVersion: string;
100
100
  /**
101
- * Absolute or registry-relative URL to the pre-built ESM bundle.
102
- * The client fetches this URL and verifies the download against `integrity`
103
- * before executing. Optional for server-only packages that ship only a
104
- * `serverBundleUrl` — in that case `integrity` must also be omitted.
101
+ * URL to the `.sh3pkg` ZIP archive for this version.
102
+ * Absolute or registry-relative. The archive contains `manifest.json`
103
+ * plus at least one of `client.js` / `server.js`.
105
104
  */
106
- bundleUrl?: string;
105
+ archiveUrl: string;
107
106
  /**
108
- * SRI integrity hash for the bundle file.
107
+ * SRI integrity hash of the archive ZIP.
109
108
  * Format: `"<algorithm>-<base64digest>"` (e.g. `"sha384-abc123..."`).
110
109
  * Algorithms: sha256, sha384 (recommended), sha512.
111
- * See: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
112
- * Omitted when the package has no client bundle.
113
110
  */
114
- integrity?: string;
115
- /**
116
- * Optional URL to the server-side bundle for shards that have a backend
117
- * component. Same resolution rules as `bundleUrl` (absolute or registry-
118
- * relative). Only present when the shard declares `serverBundle` in its
119
- * manifest.
120
- */
121
- serverBundleUrl?: string;
122
- /**
123
- * SRI integrity hash for the server bundle. Same format as `integrity`.
124
- * Optional in contract v1 for back-compat with registries that predate
125
- * server bundles. Will become required when the formal registry spec
126
- * lands (see ADR-015 proposal). When absent, the client skips the SRI
127
- * check at download time and logs a warning — the unverified bundle is
128
- * still installed.
129
- */
130
- serverIntegrity?: string;
111
+ integrity: string;
131
112
  /**
132
113
  * Other shards that must be installed and active before this package
133
114
  * can be loaded. Optional — omit if the package has no dependencies.
@@ -260,9 +241,7 @@ export interface PackageMeta {
260
241
  */
261
242
  sourceRegistry: string;
262
243
  /**
263
- * SRI hash to verify the downloaded bundle against before executing.
264
- * Must match `PackageVersion.integrity`. Omitted for server-only packages
265
- * that ship no client bundle.
244
+ * SRI hash of the archive this package was installed from. Kept for audit purposes.
266
245
  */
267
246
  integrity?: string;
268
247
  /**
@@ -270,14 +249,10 @@ export interface PackageMeta {
270
249
  * Undefined if no dependencies.
271
250
  */
272
251
  requires?: RequiredDependency[];
273
- /**
274
- * SRI hash for the server bundle. Only present when the package has a
275
- * server component.
276
- */
277
- serverIntegrity?: string;
278
- /**
279
- * Whether this package includes a server bundle that needs to be pushed
280
- * to the server after client-side installation.
281
- */
282
- hasServerBundle?: boolean;
252
+ }
253
+ /** Payload sent to the server to trigger a server-side install. */
254
+ export interface RemoteInstallRequest {
255
+ registryUrl: string;
256
+ packageId: string;
257
+ version: string;
283
258
  }
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import { describe, it, expect, beforeEach } from 'vitest';
13
13
  import { MemoryDocumentBackend } from '../documents/backends';
14
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
14
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
15
15
  import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/activate.svelte';
16
16
  import { __resetViewRegistryForTest } from '../shards/registry';
17
17
  import { __resetActionsRegistryForTest } from '../actions/registry';
@@ -25,7 +25,7 @@ describe('shell-shard programmatic verbs (integration)', () => {
25
25
  __resetActionsRegistryForTest();
26
26
  __resetAppRegistryForTest();
27
27
  __setDocumentBackend(new MemoryDocumentBackend());
28
- __setTenantId('tenant-test');
28
+ __setActiveScope('tenant-test');
29
29
  registerShard(shellShard);
30
30
  await activateShard('shell');
31
31
  });
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/activate.svelte';
5
5
  import { __resetViewRegistryForTest } from '../shards/registry';
6
6
  import { runVerbProgrammatic } from './runVerb';
@@ -19,7 +19,7 @@ describe('runVerbProgrammatic', () => {
19
19
  __resetShardRegistryForTest();
20
20
  __resetViewRegistryForTest();
21
21
  __setDocumentBackend(new MemoryDocumentBackend());
22
- __setTenantId('tenant-test');
22
+ __setActiveScope('tenant-test');
23
23
  });
24
24
  it('rejects on unknown shard', async () => {
25
25
  await expect(runVerbProgrammatic('missing', 'echo', [])).rejects.toThrow('unknown shard: missing');
@@ -30,6 +30,7 @@ import AppInfoView from './AppInfoView.svelte';
30
30
  import { spawnSatellite } from '../sh3Api/window';
31
31
  import { activeApp, getActiveApp } from '../apps/registry.svelte';
32
32
  import { returnToHome } from '../apps/lifecycle';
33
+ import { sessionState } from '../projects/session-state.svelte';
33
34
  const isTauri = typeof globalThis.__TAURI_INTERNALS__ !== 'undefined';
34
35
  export function computeAppActionDisabled(g) {
35
36
  return !g.admin || g.builtin;
@@ -111,7 +112,7 @@ async function runCheckUpdate(_ctx) {
111
112
  modalManager.open(AppUpdateAvailableModal, props);
112
113
  }
113
114
  function runPopOut(_ctx) {
114
- var _a;
115
+ var _a, _b;
115
116
  const ref = readSelection();
116
117
  if (!ref)
117
118
  return;
@@ -122,10 +123,11 @@ function runPopOut(_ctx) {
122
123
  kind: 'app',
123
124
  appId: ref.appId,
124
125
  activateShards: (_a = manifest.requiredShards) !== null && _a !== void 0 ? _a : [],
126
+ projectId: (_b = sessionState.activeProjectId) !== null && _b !== void 0 ? _b : undefined,
125
127
  });
126
128
  }
127
129
  async function runPopOutCurrent(_ctx) {
128
- var _a;
130
+ var _a, _b;
129
131
  const current = getActiveApp();
130
132
  if (!current)
131
133
  return;
@@ -136,6 +138,7 @@ async function runPopOutCurrent(_ctx) {
136
138
  kind: 'app',
137
139
  appId,
138
140
  activateShards: requiredShards,
141
+ projectId: (_b = sessionState.activeProjectId) !== null && _b !== void 0 ? _b : undefined,
139
142
  });
140
143
  }
141
144
  function runUninstall(_ctx) {
@@ -1,13 +1,13 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, __resetShardRegistryForTest } from './activate.svelte';
5
5
  import { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from '../documents/types';
6
6
  describe('ctx.browse permission gating', () => {
7
7
  beforeEach(() => {
8
8
  __resetShardRegistryForTest();
9
9
  __setDocumentBackend(new MemoryDocumentBackend());
10
- __setTenantId('tenant-a');
10
+ __setActiveScope('tenant-a');
11
11
  });
12
12
  it('is undefined when no documents permission is declared', async () => {
13
13
  let captured = null;
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, vi } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, deactivateShard, __resetShardRegistryForTest, } from './activate.svelte';
5
5
  import { __resetContributionsForTest, list, listPoints } from '../contributions';
6
6
  describe('ctx.contributions', () => {
@@ -8,7 +8,7 @@ describe('ctx.contributions', () => {
8
8
  __resetShardRegistryForTest();
9
9
  __resetContributionsForTest();
10
10
  __setDocumentBackend(new MemoryDocumentBackend());
11
- __setTenantId('tenant-a');
11
+ __setActiveScope('tenant-a');
12
12
  });
13
13
  it('is always present on ShardContext (no permission required)', async () => {
14
14
  let captured = null;
@@ -1,12 +1,12 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, registeredShards, activeShards, __resetShardRegistryForTest, erroredShards, } from './activate.svelte';
5
5
  describe('erroredShards map', () => {
6
6
  beforeEach(() => {
7
7
  __resetShardRegistryForTest();
8
8
  __setDocumentBackend(new MemoryDocumentBackend());
9
- __setTenantId('tenant-a');
9
+ __setActiveScope('tenant-a');
10
10
  });
11
11
  it('is empty after reset', () => {
12
12
  expect(erroredShards.size).toBe(0);
@@ -21,7 +21,7 @@ describe('activateShard — unwind on activation failure', () => {
21
21
  beforeEach(() => {
22
22
  __resetShardRegistryForTest();
23
23
  __setDocumentBackend(new MemoryDocumentBackend());
24
- __setTenantId('tenant-a');
24
+ __setActiveScope('tenant-a');
25
25
  });
26
26
  it('unwinds partial state and records the error when activate throws', async () => {
27
27
  const shard = {
@@ -1,13 +1,13 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, deactivateShard, __resetShardRegistryForTest } from './activate.svelte';
5
5
  import { emit } from '../keys/revocation-bus.svelte';
6
6
  describe('onKeyRevoked hook wiring', () => {
7
7
  beforeEach(() => {
8
8
  __resetShardRegistryForTest();
9
9
  __setDocumentBackend(new MemoryDocumentBackend());
10
- __setTenantId('tenant-a');
10
+ __setActiveScope('tenant-a');
11
11
  });
12
12
  it('fires onKeyRevoked when the bus emits for the shard', async () => {
13
13
  const received = [];
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, vi } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { registerShard, activateShard, __resetShardRegistryForTest, } from './activate.svelte';
5
5
  import { __resetViewRegistryForTest } from './registry';
6
6
  function programmaticVerb(name, summary, body) {
@@ -22,7 +22,7 @@ describe('ctx.listVerbs / ctx.runVerb (integration)', () => {
22
22
  __resetShardRegistryForTest();
23
23
  __resetViewRegistryForTest();
24
24
  __setDocumentBackend(new MemoryDocumentBackend());
25
- __setTenantId('tenant-test');
25
+ __setActiveScope('tenant-test');
26
26
  });
27
27
  it('listVerbs returns every verb across active shards with shardId', async () => {
28
28
  registerShard({
@@ -19,7 +19,7 @@
19
19
  import { sh3 } from '../sh3Runtime.svelte';
20
20
  import { registerView, unregisterView, registerVerb as fwRegisterVerb, unregisterVerb as fwUnregisterVerb } from './registry';
21
21
  import { makeSh3Api } from '../sh3Api/headless';
22
- import { createDocumentHandle, getTenantId, getDocumentBackend, getActiveScopeId } from '../documents';
22
+ import { createDocumentHandle, getDocumentBackend, getActiveScopeId } from '../documents';
23
23
  import { fetchEnvState, putEnvState } from '../env/client';
24
24
  import { getEnvServerUrl } from '../env/index';
25
25
  import { apiFetch } from '../transport/apiFetch';
@@ -153,7 +153,7 @@ export async function activateShard(id, opts) {
153
153
  };
154
154
  const hasBrowse = (_a = shard.manifest.permissions) === null || _a === void 0 ? void 0 : _a.includes(PERMISSION_DOCUMENTS_BROWSE);
155
155
  const browseCap = hasBrowse
156
- ? createBrowseCapability(() => getTenantId(), getDocumentBackend(), {
156
+ ? createBrowseCapability(getActiveScopeId, getDocumentBackend(), {
157
157
  canRead: (_c = (_b = shard.manifest.permissions) === null || _b === void 0 ? void 0 : _b.includes(PERMISSION_DOCUMENTS_READ)) !== null && _c !== void 0 ? _c : false,
158
158
  canWrite: (_e = (_d = shard.manifest.permissions) === null || _d === void 0 ? void 0 : _d.includes(PERMISSION_DOCUMENTS_WRITE)) !== null && _e !== void 0 ? _e : false,
159
159
  })
@@ -225,7 +225,7 @@ export async function activateShard(id, opts) {
225
225
  return checkIsAdmin();
226
226
  },
227
227
  get tenantId() {
228
- return getTenantId();
228
+ return getActiveScopeId();
229
229
  },
230
230
  getScope: () => { var _a; return (_a = scopeResolver === null || scopeResolver === void 0 ? void 0 : scopeResolver()) !== null && _a !== void 0 ? _a : 'tenant'; },
231
231
  zones: ((_f = shard.manifest.permissions) === null || _f === void 0 ? void 0 : _f.includes(PERMISSION_STATE_MANAGE))
@@ -235,7 +235,7 @@ export async function activateShard(id, opts) {
235
235
  documentPicker: browseCap
236
236
  ? createDocumentPicker(() => browseCap.listDocuments())
237
237
  : createDocumentPicker(async () => {
238
- const docs = await getDocumentBackend().list(getTenantId(), id);
238
+ const docs = await getDocumentBackend().list(getActiveScopeId(), id);
239
239
  return docs.map(d => (Object.assign(Object.assign({}, d), { shardId: id })));
240
240
  }),
241
241
  keys: ((_g = shard.manifest.permissions) === null || _g === void 0 ? void 0 : _g.includes(PERMISSION_KEYS_MINT))
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
2
  import { MemoryDocumentBackend } from '../documents/backends';
3
- import { __setDocumentBackend, __setTenantId } from '../documents/config';
3
+ import { __setDocumentBackend, __setActiveScope } from '../documents/config';
4
4
  import { __setEnvServerUrl } from '../env/index';
5
5
  import { registerShard, activateShard, __resetShardRegistryForTest, } from './activate.svelte';
6
6
  import { __resetViewRegistryForTest } from './registry';
@@ -11,7 +11,7 @@ describe('ctx.fetch', () => {
11
11
  __resetShardRegistryForTest();
12
12
  __resetViewRegistryForTest();
13
13
  __setDocumentBackend(new MemoryDocumentBackend());
14
- __setTenantId('tenant-test');
14
+ __setActiveScope('tenant-test');
15
15
  __setEnvServerUrl('https://example.com');
16
16
  });
17
17
  afterEach(() => {
@@ -69,7 +69,7 @@ describe('ctx.serverUrl', () => {
69
69
  __resetShardRegistryForTest();
70
70
  __resetViewRegistryForTest();
71
71
  __setDocumentBackend(new MemoryDocumentBackend());
72
- __setTenantId('tenant-test');
72
+ __setActiveScope('tenant-test');
73
73
  __setEnvServerUrl('https://example.com');
74
74
  });
75
75
  afterEach(() => {
@@ -100,7 +100,7 @@ describe('ctx.resolveUrl', () => {
100
100
  __resetShardRegistryForTest();
101
101
  __resetViewRegistryForTest();
102
102
  __setDocumentBackend(new MemoryDocumentBackend());
103
- __setTenantId('tenant-test');
103
+ __setActiveScope('tenant-test');
104
104
  __setEnvServerUrl('https://example.com');
105
105
  });
106
106
  afterEach(() => {