sh3-core 0.6.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/README.md +9 -0
- package/dist/Shell.svelte +283 -0
- package/dist/Shell.svelte.d.ts +5 -0
- package/dist/api.d.ts +28 -0
- package/dist/api.js +50 -0
- package/dist/app/admin/ApiKeysView.svelte +169 -0
- package/dist/app/admin/ApiKeysView.svelte.d.ts +3 -0
- package/dist/app/admin/AuthSettingsView.svelte +105 -0
- package/dist/app/admin/AuthSettingsView.svelte.d.ts +3 -0
- package/dist/app/admin/SystemView.svelte +73 -0
- package/dist/app/admin/SystemView.svelte.d.ts +3 -0
- package/dist/app/admin/UsersView.svelte +188 -0
- package/dist/app/admin/UsersView.svelte.d.ts +3 -0
- package/dist/app/admin/adminApp.d.ts +7 -0
- package/dist/app/admin/adminApp.js +25 -0
- package/dist/app/admin/adminShard.svelte.d.ts +4 -0
- package/dist/app/admin/adminShard.svelte.js +62 -0
- package/dist/app/store/InstalledView.svelte +246 -0
- package/dist/app/store/InstalledView.svelte.d.ts +3 -0
- package/dist/app/store/StoreView.svelte +522 -0
- package/dist/app/store/StoreView.svelte.d.ts +3 -0
- package/dist/app/store/storeApp.d.ts +10 -0
- package/dist/app/store/storeApp.js +26 -0
- package/dist/app/store/storeShard.svelte.d.ts +38 -0
- package/dist/app/store/storeShard.svelte.js +218 -0
- package/dist/apps/lifecycle.d.ts +42 -0
- package/dist/apps/lifecycle.js +184 -0
- package/dist/apps/registry.svelte.d.ts +40 -0
- package/dist/apps/registry.svelte.js +59 -0
- package/dist/apps/terminal/manifest.d.ts +8 -0
- package/dist/apps/terminal/manifest.js +13 -0
- package/dist/apps/terminal/terminal-app.d.ts +7 -0
- package/dist/apps/terminal/terminal-app.js +14 -0
- package/dist/apps/types.d.ts +93 -0
- package/dist/apps/types.js +10 -0
- package/dist/artifact.d.ts +32 -0
- package/dist/artifact.js +1 -0
- package/dist/assets/SH3.png +0 -0
- package/dist/assets/icons.svg +1126 -0
- package/dist/assets.d.ts +13 -0
- package/dist/auth/GuestBanner.svelte +134 -0
- package/dist/auth/GuestBanner.svelte.d.ts +3 -0
- package/dist/auth/SignInWall.svelte +203 -0
- package/dist/auth/SignInWall.svelte.d.ts +7 -0
- package/dist/auth/auth.svelte.d.ts +69 -0
- package/dist/auth/auth.svelte.js +165 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/types.d.ts +41 -0
- package/dist/auth/types.js +6 -0
- package/dist/build.d.ts +49 -0
- package/dist/build.js +236 -0
- package/dist/contract.d.ts +20 -0
- package/dist/contract.js +28 -0
- package/dist/createShell.d.ts +24 -0
- package/dist/createShell.js +131 -0
- package/dist/documents/backends.d.ts +17 -0
- package/dist/documents/backends.js +156 -0
- package/dist/documents/config.d.ts +7 -0
- package/dist/documents/config.js +27 -0
- package/dist/documents/handle.d.ts +6 -0
- package/dist/documents/handle.js +154 -0
- package/dist/documents/http-backend.d.ts +22 -0
- package/dist/documents/http-backend.js +78 -0
- package/dist/documents/index.d.ts +6 -0
- package/dist/documents/index.js +8 -0
- package/dist/documents/notifications.d.ts +9 -0
- package/dist/documents/notifications.js +39 -0
- package/dist/documents/types.d.ts +97 -0
- package/dist/documents/types.js +12 -0
- package/dist/env/client.d.ts +44 -0
- package/dist/env/client.js +106 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +1 -0
- package/dist/env/types.d.ts +12 -0
- package/dist/env/types.js +8 -0
- package/dist/host-entry.d.ts +13 -0
- package/dist/host-entry.js +17 -0
- package/dist/host.d.ts +15 -0
- package/dist/host.js +86 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +14 -0
- package/dist/layout/DragPreview.svelte +63 -0
- package/dist/layout/DragPreview.svelte.d.ts +3 -0
- package/dist/layout/LayoutRenderer.svelte +262 -0
- package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
- package/dist/layout/SlotContainer.svelte +140 -0
- package/dist/layout/SlotContainer.svelte.d.ts +8 -0
- package/dist/layout/SlotDropZone.svelte +122 -0
- package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
- package/dist/layout/drag.svelte.d.ts +45 -0
- package/dist/layout/drag.svelte.js +200 -0
- package/dist/layout/inspection.d.ts +72 -0
- package/dist/layout/inspection.js +209 -0
- package/dist/layout/ops.d.ts +100 -0
- package/dist/layout/ops.js +310 -0
- package/dist/layout/slotHostPool.svelte.d.ts +36 -0
- package/dist/layout/slotHostPool.svelte.js +229 -0
- package/dist/layout/store.svelte.d.ts +39 -0
- package/dist/layout/store.svelte.js +153 -0
- package/dist/layout/tree-walk.d.ts +15 -0
- package/dist/layout/tree-walk.js +33 -0
- package/dist/layout/types.d.ts +108 -0
- package/dist/layout/types.js +25 -0
- package/dist/migrations/shell-rename.d.ts +16 -0
- package/dist/migrations/shell-rename.js +48 -0
- package/dist/overlays/ModalFrame.svelte +87 -0
- package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
- package/dist/overlays/PopupFrame.svelte +85 -0
- package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
- package/dist/overlays/ToastItem.svelte +77 -0
- package/dist/overlays/ToastItem.svelte.d.ts +9 -0
- package/dist/overlays/focusTrap.d.ts +1 -0
- package/dist/overlays/focusTrap.js +64 -0
- package/dist/overlays/modal.d.ts +9 -0
- package/dist/overlays/modal.js +141 -0
- package/dist/overlays/popup.d.ts +9 -0
- package/dist/overlays/popup.js +108 -0
- package/dist/overlays/roots.d.ts +4 -0
- package/dist/overlays/roots.js +31 -0
- package/dist/overlays/toast.d.ts +6 -0
- package/dist/overlays/toast.js +93 -0
- package/dist/overlays/types.d.ts +31 -0
- package/dist/overlays/types.js +15 -0
- package/dist/platform/index.d.ts +10 -0
- package/dist/platform/index.js +33 -0
- package/dist/platform/tauri-backend.d.ts +15 -0
- package/dist/platform/tauri-backend.js +58 -0
- package/dist/primitives/.gitkeep +0 -0
- package/dist/primitives/ResizableSplitter.svelte +333 -0
- package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
- package/dist/primitives/TabbedPanel.svelte +305 -0
- package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
- package/dist/primitives/base.css +42 -0
- package/dist/registry/client.d.ts +74 -0
- package/dist/registry/client.js +117 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.js +14 -0
- package/dist/registry/installer.d.ts +53 -0
- package/dist/registry/installer.js +168 -0
- package/dist/registry/integrity.d.ts +32 -0
- package/dist/registry/integrity.js +92 -0
- package/dist/registry/loader.d.ts +50 -0
- package/dist/registry/loader.js +145 -0
- package/dist/registry/schema.d.ts +47 -0
- package/dist/registry/schema.js +185 -0
- package/dist/registry/storage.d.ts +37 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/types.d.ts +262 -0
- package/dist/registry/types.js +14 -0
- package/dist/server-shard/types.d.ts +67 -0
- package/dist/server-shard/types.js +13 -0
- package/dist/sh3core-shard/ShellHome.svelte +192 -0
- package/dist/sh3core-shard/ShellHome.svelte.d.ts +3 -0
- package/dist/sh3core-shard/ShellTitle.svelte +171 -0
- package/dist/sh3core-shard/ShellTitle.svelte.d.ts +3 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +2 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.js +53 -0
- package/dist/shards/activate.svelte.d.ts +52 -0
- package/dist/shards/activate.svelte.js +186 -0
- package/dist/shards/registry.d.ts +4 -0
- package/dist/shards/registry.js +28 -0
- package/dist/shards/types.d.ts +207 -0
- package/dist/shards/types.js +20 -0
- package/dist/shell-shard/InputLine.svelte +133 -0
- package/dist/shell-shard/InputLine.svelte.d.ts +11 -0
- package/dist/shell-shard/ScrollbackView.svelte +47 -0
- package/dist/shell-shard/ScrollbackView.svelte.d.ts +7 -0
- package/dist/shell-shard/Terminal.svelte +122 -0
- package/dist/shell-shard/Terminal.svelte.d.ts +8 -0
- package/dist/shell-shard/entries/PromptEntry.svelte +25 -0
- package/dist/shell-shard/entries/PromptEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/entries/RichEntry.svelte +19 -0
- package/dist/shell-shard/entries/RichEntry.svelte.d.ts +8 -0
- package/dist/shell-shard/entries/StatusEntry.svelte +22 -0
- package/dist/shell-shard/entries/StatusEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/entries/TextEntry.svelte +25 -0
- package/dist/shell-shard/entries/TextEntry.svelte.d.ts +7 -0
- package/dist/shell-shard/manifest.d.ts +2 -0
- package/dist/shell-shard/manifest.js +11 -0
- package/dist/shell-shard/protocol.d.ts +90 -0
- package/dist/shell-shard/protocol.js +11 -0
- package/dist/shell-shard/registry.d.ts +69 -0
- package/dist/shell-shard/registry.js +47 -0
- package/dist/shell-shard/rich/AppCard.svelte +25 -0
- package/dist/shell-shard/rich/AppCard.svelte.d.ts +10 -0
- package/dist/shell-shard/rich/AppsTable.svelte +29 -0
- package/dist/shell-shard/rich/AppsTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/EnvTable.svelte +27 -0
- package/dist/shell-shard/rich/EnvTable.svelte.d.ts +8 -0
- package/dist/shell-shard/rich/HelpTable.svelte +29 -0
- package/dist/shell-shard/rich/HelpTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/HistoryList.svelte +37 -0
- package/dist/shell-shard/rich/HistoryList.svelte.d.ts +9 -0
- package/dist/shell-shard/rich/ShardsTable.svelte +28 -0
- package/dist/shell-shard/rich/ShardsTable.svelte.d.ts +12 -0
- package/dist/shell-shard/rich/ViewsTable.svelte +31 -0
- package/dist/shell-shard/rich/ViewsTable.svelte.d.ts +13 -0
- package/dist/shell-shard/rich/ZoneTree.svelte +19 -0
- package/dist/shell-shard/rich/ZoneTree.svelte.d.ts +8 -0
- package/dist/shell-shard/rich/ZonesTable.svelte +27 -0
- package/dist/shell-shard/rich/ZonesTable.svelte.d.ts +11 -0
- package/dist/shell-shard/scrollback.svelte.d.ts +36 -0
- package/dist/shell-shard/scrollback.svelte.js +43 -0
- package/dist/shell-shard/session-client.svelte.d.ts +23 -0
- package/dist/shell-shard/session-client.svelte.js +120 -0
- package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
- package/dist/shell-shard/shellShard.svelte.js +139 -0
- package/dist/shell-shard/verbs/apps.d.ts +3 -0
- package/dist/shell-shard/verbs/apps.js +50 -0
- package/dist/shell-shard/verbs/clear.d.ts +2 -0
- package/dist/shell-shard/verbs/clear.js +7 -0
- package/dist/shell-shard/verbs/help.d.ts +2 -0
- package/dist/shell-shard/verbs/help.js +21 -0
- package/dist/shell-shard/verbs/history.d.ts +2 -0
- package/dist/shell-shard/verbs/history.js +20 -0
- package/dist/shell-shard/verbs/index.d.ts +2 -0
- package/dist/shell-shard/verbs/index.js +29 -0
- package/dist/shell-shard/verbs/session.d.ts +5 -0
- package/dist/shell-shard/verbs/session.js +65 -0
- package/dist/shell-shard/verbs/shards.d.ts +2 -0
- package/dist/shell-shard/verbs/shards.js +14 -0
- package/dist/shell-shard/verbs/views.d.ts +4 -0
- package/dist/shell-shard/verbs/views.js +90 -0
- package/dist/shell-shard/verbs/zones.d.ts +3 -0
- package/dist/shell-shard/verbs/zones.js +38 -0
- package/dist/shellRuntime.svelte.d.ts +27 -0
- package/dist/shellRuntime.svelte.js +27 -0
- package/dist/state/backends.d.ts +26 -0
- package/dist/state/backends.js +99 -0
- package/dist/state/manage.d.ts +14 -0
- package/dist/state/manage.js +40 -0
- package/dist/state/types.d.ts +55 -0
- package/dist/state/types.js +17 -0
- package/dist/state/zones.svelte.d.ts +53 -0
- package/dist/state/zones.svelte.js +141 -0
- package/dist/theme.d.ts +28 -0
- package/dist/theme.js +92 -0
- package/dist/tokens.css +102 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +60 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone configuration — module-level singletons.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the __setBackend pattern in state/zones.svelte.ts. The host
|
|
5
|
+
* calls __setTenantId and __setDocumentBackend before bootstrap() to
|
|
6
|
+
* configure multi-tenancy and swap backends (e.g. Tauri FS).
|
|
7
|
+
*
|
|
8
|
+
* Defaults: tenantId='local' (single-user self-hosted), backend=IndexedDB.
|
|
9
|
+
*/
|
|
10
|
+
import { IndexedDBDocumentBackend } from './backends';
|
|
11
|
+
const DEFAULT_TENANT = 'local';
|
|
12
|
+
let tenantId = DEFAULT_TENANT;
|
|
13
|
+
let backend = new IndexedDBDocumentBackend();
|
|
14
|
+
export function getTenantId() {
|
|
15
|
+
return tenantId;
|
|
16
|
+
}
|
|
17
|
+
export function getDocumentBackend() {
|
|
18
|
+
return backend;
|
|
19
|
+
}
|
|
20
|
+
/** Host-only. Set the tenant id before bootstrap(). */
|
|
21
|
+
export function __setTenantId(id) {
|
|
22
|
+
tenantId = id;
|
|
23
|
+
}
|
|
24
|
+
/** Host-only. Swap the document backend before bootstrap(). */
|
|
25
|
+
export function __setDocumentBackend(b) {
|
|
26
|
+
backend = b;
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DocumentBackend, DocumentHandle, DocumentHandleOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a document handle scoped to a tenant, shard, and file filter.
|
|
4
|
+
* The framework calls this from `ShardContext.documents()`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createDocumentHandle(tenantId: string, shardId: string, backend: DocumentBackend, options: DocumentHandleOptions): DocumentHandle;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document handle — the shard-facing API for the document zone.
|
|
3
|
+
*
|
|
4
|
+
* `createDocumentHandle` binds tenant, shard, backend, and options so
|
|
5
|
+
* shard code only deals in paths. Supports explicit save, autosave
|
|
6
|
+
* with debounce, and change notifications via the global emitter.
|
|
7
|
+
*/
|
|
8
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
9
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
10
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
11
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
12
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
13
|
+
};
|
|
14
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
15
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
16
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
17
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
18
|
+
};
|
|
19
|
+
var _AutosaveControllerImpl_instances, _AutosaveControllerImpl_handle, _AutosaveControllerImpl_path, _AutosaveControllerImpl_debounceMs, _AutosaveControllerImpl_pending, _AutosaveControllerImpl_timer, _AutosaveControllerImpl_dirty, _AutosaveControllerImpl_disposed, _AutosaveControllerImpl_scheduleFlush, _AutosaveControllerImpl_clearTimer;
|
|
20
|
+
import { documentChanges } from './notifications';
|
|
21
|
+
const DEFAULT_DEBOUNCE_MS = 1000;
|
|
22
|
+
/**
|
|
23
|
+
* Create a document handle scoped to a tenant, shard, and file filter.
|
|
24
|
+
* The framework calls this from `ShardContext.documents()`.
|
|
25
|
+
*/
|
|
26
|
+
export function createDocumentHandle(tenantId, shardId, backend, options) {
|
|
27
|
+
const controllers = new Set();
|
|
28
|
+
const unsubscribers = new Set();
|
|
29
|
+
function matchesExtensions(path) {
|
|
30
|
+
if (!options.extensions || options.extensions.length === 0)
|
|
31
|
+
return true;
|
|
32
|
+
return options.extensions.some((ext) => path.endsWith(ext));
|
|
33
|
+
}
|
|
34
|
+
function emitChange(type, path) {
|
|
35
|
+
documentChanges.emit({ type, path, tenantId, shardId });
|
|
36
|
+
}
|
|
37
|
+
const handle = {
|
|
38
|
+
async list() {
|
|
39
|
+
const all = await backend.list(tenantId, shardId);
|
|
40
|
+
if (!options.extensions || options.extensions.length === 0)
|
|
41
|
+
return all;
|
|
42
|
+
return all.filter((meta) => matchesExtensions(meta.path));
|
|
43
|
+
},
|
|
44
|
+
async read(path) {
|
|
45
|
+
const content = await backend.read(tenantId, shardId, path);
|
|
46
|
+
if (content === null)
|
|
47
|
+
return null;
|
|
48
|
+
// Phase 1: text format only. Binary returns as-is from the backend
|
|
49
|
+
// but the handle types it as string for text-format handles.
|
|
50
|
+
return typeof content === 'string' ? content : new TextDecoder().decode(content);
|
|
51
|
+
},
|
|
52
|
+
async write(path, content) {
|
|
53
|
+
const existed = await backend.exists(tenantId, shardId, path);
|
|
54
|
+
await backend.write(tenantId, shardId, path, content);
|
|
55
|
+
emitChange(existed ? 'update' : 'create', path);
|
|
56
|
+
},
|
|
57
|
+
async delete(path) {
|
|
58
|
+
await backend.delete(tenantId, shardId, path);
|
|
59
|
+
emitChange('delete', path);
|
|
60
|
+
},
|
|
61
|
+
async exists(path) {
|
|
62
|
+
return backend.exists(tenantId, shardId, path);
|
|
63
|
+
},
|
|
64
|
+
watch(callback) {
|
|
65
|
+
// Subscribe to global emitter, filtered to this handle's scope.
|
|
66
|
+
const unsub = documentChanges.subscribe((change) => {
|
|
67
|
+
if (change.tenantId !== tenantId)
|
|
68
|
+
return;
|
|
69
|
+
if (change.shardId !== shardId)
|
|
70
|
+
return;
|
|
71
|
+
if (!matchesExtensions(change.path))
|
|
72
|
+
return;
|
|
73
|
+
callback(change);
|
|
74
|
+
});
|
|
75
|
+
unsubscribers.add(unsub);
|
|
76
|
+
return () => {
|
|
77
|
+
unsub();
|
|
78
|
+
unsubscribers.delete(unsub);
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
autosave(path, opts) {
|
|
82
|
+
var _a;
|
|
83
|
+
const ctrl = new AutosaveControllerImpl(handle, path, (_a = opts === null || opts === void 0 ? void 0 : opts.debounceMs) !== null && _a !== void 0 ? _a : DEFAULT_DEBOUNCE_MS);
|
|
84
|
+
controllers.add(ctrl);
|
|
85
|
+
return ctrl;
|
|
86
|
+
},
|
|
87
|
+
async dispose() {
|
|
88
|
+
// Flush and dispose all active autosave controllers.
|
|
89
|
+
const flushes = [...controllers].map((c) => c.dispose());
|
|
90
|
+
await Promise.all(flushes);
|
|
91
|
+
controllers.clear();
|
|
92
|
+
// Unsubscribe all watchers.
|
|
93
|
+
for (const unsub of unsubscribers)
|
|
94
|
+
unsub();
|
|
95
|
+
unsubscribers.clear();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
return handle;
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// AutosaveController implementation
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
class AutosaveControllerImpl {
|
|
104
|
+
constructor(handle, path, debounceMs) {
|
|
105
|
+
_AutosaveControllerImpl_instances.add(this);
|
|
106
|
+
_AutosaveControllerImpl_handle.set(this, void 0);
|
|
107
|
+
_AutosaveControllerImpl_path.set(this, void 0);
|
|
108
|
+
_AutosaveControllerImpl_debounceMs.set(this, void 0);
|
|
109
|
+
_AutosaveControllerImpl_pending.set(this, null);
|
|
110
|
+
_AutosaveControllerImpl_timer.set(this, null);
|
|
111
|
+
_AutosaveControllerImpl_dirty.set(this, false);
|
|
112
|
+
_AutosaveControllerImpl_disposed.set(this, false);
|
|
113
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_handle, handle, "f");
|
|
114
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_path, path, "f");
|
|
115
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_debounceMs, debounceMs, "f");
|
|
116
|
+
}
|
|
117
|
+
get dirty() {
|
|
118
|
+
return __classPrivateFieldGet(this, _AutosaveControllerImpl_dirty, "f");
|
|
119
|
+
}
|
|
120
|
+
update(content) {
|
|
121
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_disposed, "f"))
|
|
122
|
+
return;
|
|
123
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_pending, content, "f");
|
|
124
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_dirty, true, "f");
|
|
125
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_scheduleFlush).call(this);
|
|
126
|
+
}
|
|
127
|
+
async flush() {
|
|
128
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_clearTimer).call(this);
|
|
129
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_pending, "f") !== null) {
|
|
130
|
+
const content = __classPrivateFieldGet(this, _AutosaveControllerImpl_pending, "f");
|
|
131
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_pending, null, "f");
|
|
132
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_dirty, false, "f");
|
|
133
|
+
await __classPrivateFieldGet(this, _AutosaveControllerImpl_handle, "f").write(__classPrivateFieldGet(this, _AutosaveControllerImpl_path, "f"), content);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async dispose() {
|
|
137
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_disposed, "f"))
|
|
138
|
+
return;
|
|
139
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_disposed, true, "f");
|
|
140
|
+
await this.flush();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
_AutosaveControllerImpl_handle = new WeakMap(), _AutosaveControllerImpl_path = new WeakMap(), _AutosaveControllerImpl_debounceMs = new WeakMap(), _AutosaveControllerImpl_pending = new WeakMap(), _AutosaveControllerImpl_timer = new WeakMap(), _AutosaveControllerImpl_dirty = new WeakMap(), _AutosaveControllerImpl_disposed = new WeakMap(), _AutosaveControllerImpl_instances = new WeakSet(), _AutosaveControllerImpl_scheduleFlush = function _AutosaveControllerImpl_scheduleFlush() {
|
|
144
|
+
__classPrivateFieldGet(this, _AutosaveControllerImpl_instances, "m", _AutosaveControllerImpl_clearTimer).call(this);
|
|
145
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, setTimeout(() => {
|
|
146
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, null, "f");
|
|
147
|
+
void this.flush();
|
|
148
|
+
}, __classPrivateFieldGet(this, _AutosaveControllerImpl_debounceMs, "f")), "f");
|
|
149
|
+
}, _AutosaveControllerImpl_clearTimer = function _AutosaveControllerImpl_clearTimer() {
|
|
150
|
+
if (__classPrivateFieldGet(this, _AutosaveControllerImpl_timer, "f") !== null) {
|
|
151
|
+
clearTimeout(__classPrivateFieldGet(this, _AutosaveControllerImpl_timer, "f"));
|
|
152
|
+
__classPrivateFieldSet(this, _AutosaveControllerImpl_timer, null, "f");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP document backend — connects to sh3-server's document API.
|
|
3
|
+
*
|
|
4
|
+
* Implements the DocumentBackend interface over HTTP. Read operations
|
|
5
|
+
* are unauthenticated; write operations send the API key as a Bearer
|
|
6
|
+
* token. This is the web-hosted equivalent of the Tauri filesystem
|
|
7
|
+
* backend.
|
|
8
|
+
*/
|
|
9
|
+
import type { DocumentBackend, DocumentMeta } from './types';
|
|
10
|
+
export declare class HttpDocumentBackend implements DocumentBackend {
|
|
11
|
+
#private;
|
|
12
|
+
/**
|
|
13
|
+
* @param baseUrl - The server origin (e.g. 'http://localhost:3000' or window.location.origin).
|
|
14
|
+
* @param apiKey - Optional API key for write operations.
|
|
15
|
+
*/
|
|
16
|
+
constructor(baseUrl: string, apiKey?: string);
|
|
17
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
18
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
19
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
20
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
21
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP document backend — connects to sh3-server's document API.
|
|
3
|
+
*
|
|
4
|
+
* Implements the DocumentBackend interface over HTTP. Read operations
|
|
5
|
+
* are unauthenticated; write operations send the API key as a Bearer
|
|
6
|
+
* token. This is the web-hosted equivalent of the Tauri filesystem
|
|
7
|
+
* backend.
|
|
8
|
+
*/
|
|
9
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
10
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
11
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
12
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
13
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
14
|
+
};
|
|
15
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
16
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
17
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
18
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
19
|
+
};
|
|
20
|
+
var _HttpDocumentBackend_instances, _HttpDocumentBackend_baseUrl, _HttpDocumentBackend_apiKey, _HttpDocumentBackend_authHeaders;
|
|
21
|
+
export class HttpDocumentBackend {
|
|
22
|
+
/**
|
|
23
|
+
* @param baseUrl - The server origin (e.g. 'http://localhost:3000' or window.location.origin).
|
|
24
|
+
* @param apiKey - Optional API key for write operations.
|
|
25
|
+
*/
|
|
26
|
+
constructor(baseUrl, apiKey) {
|
|
27
|
+
_HttpDocumentBackend_instances.add(this);
|
|
28
|
+
_HttpDocumentBackend_baseUrl.set(this, void 0);
|
|
29
|
+
_HttpDocumentBackend_apiKey.set(this, void 0);
|
|
30
|
+
// Strip trailing slash
|
|
31
|
+
__classPrivateFieldSet(this, _HttpDocumentBackend_baseUrl, baseUrl.replace(/\/$/, ''), "f");
|
|
32
|
+
__classPrivateFieldSet(this, _HttpDocumentBackend_apiKey, apiKey, "f");
|
|
33
|
+
}
|
|
34
|
+
async read(tenantId, shardId, path) {
|
|
35
|
+
var _a;
|
|
36
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
37
|
+
const res = await fetch(url);
|
|
38
|
+
if (res.status === 404)
|
|
39
|
+
return null;
|
|
40
|
+
if (!res.ok)
|
|
41
|
+
throw new Error(`Document read failed: ${res.status}`);
|
|
42
|
+
const ct = (_a = res.headers.get('content-type')) !== null && _a !== void 0 ? _a : '';
|
|
43
|
+
if (ct.includes('application/octet-stream')) {
|
|
44
|
+
return res.arrayBuffer();
|
|
45
|
+
}
|
|
46
|
+
return res.text();
|
|
47
|
+
}
|
|
48
|
+
async write(tenantId, shardId, path, content) {
|
|
49
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
50
|
+
const headers = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _HttpDocumentBackend_instances, "m", _HttpDocumentBackend_authHeaders).call(this)), { 'Content-Type': typeof content === 'string' ? 'text/plain' : 'application/octet-stream' });
|
|
51
|
+
const res = await fetch(url, { method: 'PUT', headers, body: content });
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`Document write failed: ${res.status}`);
|
|
54
|
+
}
|
|
55
|
+
async delete(tenantId, shardId, path) {
|
|
56
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
57
|
+
const res = await fetch(url, { method: 'DELETE', headers: __classPrivateFieldGet(this, _HttpDocumentBackend_instances, "m", _HttpDocumentBackend_authHeaders).call(this) });
|
|
58
|
+
if (!res.ok)
|
|
59
|
+
throw new Error(`Document delete failed: ${res.status}`);
|
|
60
|
+
}
|
|
61
|
+
async list(tenantId, shardId) {
|
|
62
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}`;
|
|
63
|
+
const res = await fetch(url);
|
|
64
|
+
if (!res.ok)
|
|
65
|
+
throw new Error(`Document list failed: ${res.status}`);
|
|
66
|
+
return res.json();
|
|
67
|
+
}
|
|
68
|
+
async exists(tenantId, shardId, path) {
|
|
69
|
+
const url = `${__classPrivateFieldGet(this, _HttpDocumentBackend_baseUrl, "f")}/api/docs/${tenantId}/${shardId}/${path}`;
|
|
70
|
+
const res = await fetch(url, { method: 'HEAD' });
|
|
71
|
+
return res.ok;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
_HttpDocumentBackend_baseUrl = new WeakMap(), _HttpDocumentBackend_apiKey = new WeakMap(), _HttpDocumentBackend_instances = new WeakSet(), _HttpDocumentBackend_authHeaders = function _HttpDocumentBackend_authHeaders() {
|
|
75
|
+
if (!__classPrivateFieldGet(this, _HttpDocumentBackend_apiKey, "f"))
|
|
76
|
+
return {};
|
|
77
|
+
return { Authorization: `Bearer ${__classPrivateFieldGet(this, _HttpDocumentBackend_apiKey, "f")}` };
|
|
78
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { DocumentFormat, DocumentHandleOptions, DocumentMeta, DocumentChange, DocumentBackend, DocumentHandle, AutosaveController, } from './types';
|
|
2
|
+
export { MemoryDocumentBackend, IndexedDBDocumentBackend } from './backends';
|
|
3
|
+
export { HttpDocumentBackend } from './http-backend';
|
|
4
|
+
export { createDocumentHandle } from './handle';
|
|
5
|
+
export { documentChanges } from './notifications';
|
|
6
|
+
export { getTenantId, getDocumentBackend, __setTenantId, __setDocumentBackend, } from './config';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone barrel — internal re-exports.
|
|
3
|
+
*/
|
|
4
|
+
export { MemoryDocumentBackend, IndexedDBDocumentBackend } from './backends';
|
|
5
|
+
export { HttpDocumentBackend } from './http-backend';
|
|
6
|
+
export { createDocumentHandle } from './handle';
|
|
7
|
+
export { documentChanges } from './notifications';
|
|
8
|
+
export { getTenantId, getDocumentBackend, __setTenantId, __setDocumentBackend, } from './config';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DocumentChange } from './types';
|
|
2
|
+
type Listener = (change: DocumentChange) => void;
|
|
3
|
+
declare class DocumentChangeEmitter {
|
|
4
|
+
#private;
|
|
5
|
+
subscribe(fn: Listener): () => void;
|
|
6
|
+
emit(change: DocumentChange): void;
|
|
7
|
+
}
|
|
8
|
+
export declare const documentChanges: DocumentChangeEmitter;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document change notification emitter — in-process pub/sub.
|
|
3
|
+
*
|
|
4
|
+
* A single global emitter connects document handles so that a write
|
|
5
|
+
* from one handle (e.g. an editor shard saving a file) is visible to
|
|
6
|
+
* another handle watching the same scope (e.g. a preview shard).
|
|
7
|
+
*
|
|
8
|
+
* Cross-tab sync (BroadcastChannel) is deferred — this emitter is
|
|
9
|
+
* in-process only.
|
|
10
|
+
*/
|
|
11
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
12
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
13
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
14
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
15
|
+
};
|
|
16
|
+
var _DocumentChangeEmitter_listeners;
|
|
17
|
+
class DocumentChangeEmitter {
|
|
18
|
+
constructor() {
|
|
19
|
+
_DocumentChangeEmitter_listeners.set(this, new Set());
|
|
20
|
+
}
|
|
21
|
+
subscribe(fn) {
|
|
22
|
+
__classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f").add(fn);
|
|
23
|
+
return () => {
|
|
24
|
+
__classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f").delete(fn);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
emit(change) {
|
|
28
|
+
for (const fn of __classPrivateFieldGet(this, _DocumentChangeEmitter_listeners, "f")) {
|
|
29
|
+
try {
|
|
30
|
+
fn(change);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.error('SH3: document change listener threw', e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
_DocumentChangeEmitter_listeners = new WeakMap();
|
|
39
|
+
export const documentChanges = new DocumentChangeEmitter();
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format hint for document content. Determines whether reads return a string
|
|
3
|
+
* (`text`) or an `ArrayBuffer` (`binary`).
|
|
4
|
+
*/
|
|
5
|
+
export type DocumentFormat = 'text' | 'binary';
|
|
6
|
+
/**
|
|
7
|
+
* Options passed to `ctx.documents()` to scope the handle. The handle
|
|
8
|
+
* only operates on files matching the declared extensions (if provided).
|
|
9
|
+
* Omitting `extensions` means "all files."
|
|
10
|
+
*/
|
|
11
|
+
export interface DocumentHandleOptions {
|
|
12
|
+
format: DocumentFormat;
|
|
13
|
+
/** File extensions this handle operates on, e.g. ['.guml', '.md']. */
|
|
14
|
+
extensions?: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Metadata about a stored document. */
|
|
17
|
+
export interface DocumentMeta {
|
|
18
|
+
path: string;
|
|
19
|
+
size: number;
|
|
20
|
+
/** Last modified timestamp in epoch milliseconds. */
|
|
21
|
+
lastModified: number;
|
|
22
|
+
}
|
|
23
|
+
/** Change notification payload delivered to watch callbacks. */
|
|
24
|
+
export interface DocumentChange {
|
|
25
|
+
type: 'create' | 'update' | 'delete';
|
|
26
|
+
path: string;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
shardId: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* File-oriented backend for the document zone.
|
|
32
|
+
*
|
|
33
|
+
* Every method is tenant- and shard-scoped. The backend is responsible
|
|
34
|
+
* for enforcing isolation: a read for tenant A must never return
|
|
35
|
+
* content belonging to tenant B.
|
|
36
|
+
*
|
|
37
|
+
* Logical storage namespace: /{tenantId}/{shardId}/{path}
|
|
38
|
+
*/
|
|
39
|
+
export interface DocumentBackend {
|
|
40
|
+
/**
|
|
41
|
+
* Read a document. Returns the content string (text format) or ArrayBuffer
|
|
42
|
+
* (binary format), or null if the document does not exist.
|
|
43
|
+
*/
|
|
44
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
45
|
+
/** Write (create or overwrite) a document with the given content. */
|
|
46
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
47
|
+
/** Delete a document. No-op if the document does not exist. */
|
|
48
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
49
|
+
/** List all documents stored for this tenant + shard combination. */
|
|
50
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
51
|
+
/** Return true if the document at `path` exists. */
|
|
52
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Shard-facing document handle returned by `ctx.documents()`. Binds
|
|
56
|
+
* the tenant, shard, and backend so shard code only deals in paths.
|
|
57
|
+
*/
|
|
58
|
+
export interface DocumentHandle {
|
|
59
|
+
/** List documents matching the handle's extensions filter. */
|
|
60
|
+
list(): Promise<DocumentMeta[]>;
|
|
61
|
+
/** Read a document by path. Returns null if not found. */
|
|
62
|
+
read(path: string): Promise<string | null>;
|
|
63
|
+
/** Write (create or overwrite) a document. Explicit save. */
|
|
64
|
+
write(path: string, content: string): Promise<void>;
|
|
65
|
+
/** Delete a document. */
|
|
66
|
+
delete(path: string): Promise<void>;
|
|
67
|
+
/** Check existence without reading content. */
|
|
68
|
+
exists(path: string): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to change notifications within this handle's scope.
|
|
71
|
+
* Returns an unsubscribe function.
|
|
72
|
+
*/
|
|
73
|
+
watch(callback: (change: DocumentChange) => void): () => void;
|
|
74
|
+
/**
|
|
75
|
+
* Enable autosave for a document. Returns a controller that accepts
|
|
76
|
+
* content updates and debounces writes to the backend.
|
|
77
|
+
*/
|
|
78
|
+
autosave(path: string, options?: {
|
|
79
|
+
debounceMs?: number;
|
|
80
|
+
}): AutosaveController;
|
|
81
|
+
/**
|
|
82
|
+
* Flush all active autosave controllers and release resources.
|
|
83
|
+
* Called by the framework on shard deactivation.
|
|
84
|
+
*/
|
|
85
|
+
dispose(): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
/** Controller for autosaved documents. */
|
|
88
|
+
export interface AutosaveController {
|
|
89
|
+
/** Feed new content. A debounced write is scheduled automatically. */
|
|
90
|
+
update(content: string): void;
|
|
91
|
+
/** Force an immediate flush of pending content. */
|
|
92
|
+
flush(): Promise<void>;
|
|
93
|
+
/** Stop autosaving. Flushes pending content first. */
|
|
94
|
+
dispose(): Promise<void>;
|
|
95
|
+
/** Whether there are unflushed changes. */
|
|
96
|
+
readonly dirty: boolean;
|
|
97
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone types — the file-oriented storage subsystem.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the KV state zones (workspace/user) which store opaque blobs
|
|
5
|
+
* per shard, the document zone deals in named files: read, write, list,
|
|
6
|
+
* watch. Every operation is tenant-scoped for multi-user isolation and
|
|
7
|
+
* async because file I/O is inherently async regardless of backend.
|
|
8
|
+
*
|
|
9
|
+
* The document zone is a parallel subsystem — it does not extend
|
|
10
|
+
* ZoneName or ZoneSchema. Shards access it via `ctx.documents()`.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env state client — fetches and updates per-shard environment state
|
|
3
|
+
* from the server.
|
|
4
|
+
*/
|
|
5
|
+
/** Configure the server URL for env state operations. */
|
|
6
|
+
export declare function __setEnvServerUrl(url: string): void;
|
|
7
|
+
/** Return the configured server URL. */
|
|
8
|
+
export declare function getEnvServerUrl(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch env state for a shard from the server.
|
|
11
|
+
* Returns an empty object if the server has no stored state.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fetchEnvState(shardId: string): Promise<Record<string, unknown>>;
|
|
14
|
+
/**
|
|
15
|
+
* Write env state for a shard to the server. Requires admin auth.
|
|
16
|
+
* Throws if not admin or if the server rejects the write.
|
|
17
|
+
*/
|
|
18
|
+
export declare function putEnvState(shardId: string, state: Record<string, unknown>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Install a package on the server via multipart upload.
|
|
21
|
+
* The client has already fetched and integrity-verified the bundle.
|
|
22
|
+
*/
|
|
23
|
+
export declare function serverInstallPackage(manifest: Record<string, unknown>, clientBundle: ArrayBuffer): Promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
id: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Uninstall a package from the server.
|
|
30
|
+
*/
|
|
31
|
+
export declare function serverUninstallPackage(id: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the list of packages installed on the server.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchServerPackages(): Promise<Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
type: string;
|
|
38
|
+
label: string;
|
|
39
|
+
version: string;
|
|
40
|
+
bundleUrl: string;
|
|
41
|
+
sourceRegistry?: string;
|
|
42
|
+
contractVersion?: string;
|
|
43
|
+
installedAt?: string;
|
|
44
|
+
}>>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env state client — fetches and updates per-shard environment state
|
|
3
|
+
* from the server.
|
|
4
|
+
*/
|
|
5
|
+
import { getAuthHeader, isAdmin } from '../auth/index';
|
|
6
|
+
/** Server base URL, set once during configuration. */
|
|
7
|
+
let serverUrl = '';
|
|
8
|
+
/** Configure the server URL for env state operations. */
|
|
9
|
+
export function __setEnvServerUrl(url) {
|
|
10
|
+
serverUrl = url;
|
|
11
|
+
}
|
|
12
|
+
/** Return the configured server URL. */
|
|
13
|
+
export function getEnvServerUrl() {
|
|
14
|
+
return serverUrl;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Fetch env state for a shard from the server.
|
|
18
|
+
* Returns an empty object if the server has no stored state.
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchEnvState(shardId) {
|
|
21
|
+
const res = await fetch(`${serverUrl}/api/env-state/${encodeURIComponent(shardId)}`);
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
console.warn(`[sh3] Failed to fetch env state for "${shardId}": HTTP ${res.status}`);
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
return await res.json();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Write env state for a shard to the server. Requires admin auth.
|
|
30
|
+
* Throws if not admin or if the server rejects the write.
|
|
31
|
+
*/
|
|
32
|
+
export async function putEnvState(shardId, state) {
|
|
33
|
+
var _a;
|
|
34
|
+
if (!isAdmin()) {
|
|
35
|
+
throw new Error('Cannot update env state: not elevated to admin');
|
|
36
|
+
}
|
|
37
|
+
const auth = getAuthHeader();
|
|
38
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
39
|
+
if (auth)
|
|
40
|
+
headers['Authorization'] = auth;
|
|
41
|
+
const res = await fetch(`${serverUrl}/api/env-state/${encodeURIComponent(shardId)}`, {
|
|
42
|
+
method: 'PUT',
|
|
43
|
+
headers,
|
|
44
|
+
body: JSON.stringify(state),
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const body = await res.json().catch(() => ({}));
|
|
48
|
+
throw new Error(`Env state update failed: HTTP ${res.status} — ${(_a = body.error) !== null && _a !== void 0 ? _a : 'unknown'}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Install a package on the server via multipart upload.
|
|
53
|
+
* The client has already fetched and integrity-verified the bundle.
|
|
54
|
+
*/
|
|
55
|
+
export async function serverInstallPackage(manifest, clientBundle) {
|
|
56
|
+
var _a;
|
|
57
|
+
if (!isAdmin())
|
|
58
|
+
throw new Error('Cannot install: not elevated to admin');
|
|
59
|
+
const auth = getAuthHeader();
|
|
60
|
+
const form = new FormData();
|
|
61
|
+
form.append('manifest', new Blob([JSON.stringify(manifest)], { type: 'application/json' }), 'manifest.json');
|
|
62
|
+
form.append('client', new Blob([clientBundle], { type: 'application/javascript' }), 'client.js');
|
|
63
|
+
const headers = {};
|
|
64
|
+
if (auth)
|
|
65
|
+
headers['Authorization'] = auth;
|
|
66
|
+
const res = await fetch(`${serverUrl}/api/packages/install`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers,
|
|
69
|
+
body: form,
|
|
70
|
+
});
|
|
71
|
+
const body = await res.json();
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
return { ok: false, id: String(manifest.id), error: (_a = body.error) !== null && _a !== void 0 ? _a : `HTTP ${res.status}` };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, id: String(manifest.id) };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Uninstall a package from the server.
|
|
79
|
+
*/
|
|
80
|
+
export async function serverUninstallPackage(id) {
|
|
81
|
+
var _a;
|
|
82
|
+
if (!isAdmin())
|
|
83
|
+
throw new Error('Cannot uninstall: not elevated to admin');
|
|
84
|
+
const auth = getAuthHeader();
|
|
85
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
86
|
+
if (auth)
|
|
87
|
+
headers['Authorization'] = auth;
|
|
88
|
+
const res = await fetch(`${serverUrl}/api/packages/uninstall`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers,
|
|
91
|
+
body: JSON.stringify({ id }),
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
const body = await res.json().catch(() => ({}));
|
|
95
|
+
throw new Error(`Uninstall failed: HTTP ${res.status} — ${(_a = body.error) !== null && _a !== void 0 ? _a : 'unknown'}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fetch the list of packages installed on the server.
|
|
100
|
+
*/
|
|
101
|
+
export async function fetchServerPackages() {
|
|
102
|
+
const res = await fetch(`${serverUrl}/api/packages`);
|
|
103
|
+
if (!res.ok)
|
|
104
|
+
return [];
|
|
105
|
+
return await res.json();
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { __setEnvServerUrl, getEnvServerUrl, fetchEnvState, putEnvState, serverInstallPackage, serverUninstallPackage, fetchServerPackages, } from './client';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment state types — server-authoritative per-shard config.
|
|
3
|
+
*
|
|
4
|
+
* Env state is fetched once at shard activation and written back via
|
|
5
|
+
* explicit admin action. Not a state zone — backed by the server, not
|
|
6
|
+
* localStorage.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Reactive environment state proxy. Reads are local (from hydrated
|
|
10
|
+
* snapshot). Writes go through envUpdate().
|
|
11
|
+
*/
|
|
12
|
+
export type EnvState<T extends Record<string, unknown>> = T;
|