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
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time helpers for SH3 package authors.
|
|
3
|
+
*
|
|
4
|
+
* Usage in a downstream vite.config.ts:
|
|
5
|
+
*
|
|
6
|
+
* import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
7
|
+
* import { sh3CssInline } from 'sh3-core/build';
|
|
8
|
+
*
|
|
9
|
+
* export default defineConfig({
|
|
10
|
+
* plugins: [svelte(), sh3CssInline()],
|
|
11
|
+
* build: {
|
|
12
|
+
* lib: { entry: 'src/main.ts', formats: ['es'], fileName: 'bundle' },
|
|
13
|
+
* rollupOptions: {
|
|
14
|
+
* external: ['sh3', 'svelte', 'svelte/internal/client'],
|
|
15
|
+
* },
|
|
16
|
+
* },
|
|
17
|
+
* });
|
|
18
|
+
*/
|
|
19
|
+
import type { Plugin } from 'vite';
|
|
20
|
+
import type { ArtifactManifest } from './artifact';
|
|
21
|
+
/**
|
|
22
|
+
* Vite plugin that inlines extracted CSS into the JS bundle.
|
|
23
|
+
*
|
|
24
|
+
* Vite's lib mode writes CSS to a separate file outside the Rollup
|
|
25
|
+
* bundle pipeline, so Rollup hooks like `generateBundle` never see it.
|
|
26
|
+
* This plugin uses `closeBundle` — which runs after all files are on
|
|
27
|
+
* disk — to find CSS files, inject their contents into the JS entry
|
|
28
|
+
* as a self-executing `<style>` injector, and delete the CSS files.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sh3CssInline(): Plugin;
|
|
31
|
+
/**
|
|
32
|
+
* Options for the sh3Artifact plugin.
|
|
33
|
+
*/
|
|
34
|
+
export interface Sh3ArtifactOptions {
|
|
35
|
+
/** Path to pre-built server bundle. If provided, gets copied as server.js. */
|
|
36
|
+
serverEntry?: string;
|
|
37
|
+
/** Override manifest fields not extractable from code (description, author, requires). */
|
|
38
|
+
manifest?: Partial<Pick<ArtifactManifest, 'description' | 'author' | 'requires'>>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Vite plugin that produces a distributable artifact directory after build.
|
|
42
|
+
*
|
|
43
|
+
* After Vite's lib build completes, this plugin:
|
|
44
|
+
* 1. Finds the entry chunk and renames it to client.js
|
|
45
|
+
* 2. Optionally copies a pre-built server bundle as server.js
|
|
46
|
+
* 3. Extracts manifest fields (id, label, version, type) from the source
|
|
47
|
+
* 4. Writes manifest.json alongside the bundles
|
|
48
|
+
*/
|
|
49
|
+
export declare function sh3Artifact(options?: Sh3ArtifactOptions): Plugin;
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time helpers for SH3 package authors.
|
|
3
|
+
*
|
|
4
|
+
* Usage in a downstream vite.config.ts:
|
|
5
|
+
*
|
|
6
|
+
* import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
7
|
+
* import { sh3CssInline } from 'sh3-core/build';
|
|
8
|
+
*
|
|
9
|
+
* export default defineConfig({
|
|
10
|
+
* plugins: [svelte(), sh3CssInline()],
|
|
11
|
+
* build: {
|
|
12
|
+
* lib: { entry: 'src/main.ts', formats: ['es'], fileName: 'bundle' },
|
|
13
|
+
* rollupOptions: {
|
|
14
|
+
* external: ['sh3', 'svelte', 'svelte/internal/client'],
|
|
15
|
+
* },
|
|
16
|
+
* },
|
|
17
|
+
* });
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, writeFileSync, unlinkSync, readdirSync, copyFileSync, existsSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
/**
|
|
22
|
+
* Vite plugin that inlines extracted CSS into the JS bundle.
|
|
23
|
+
*
|
|
24
|
+
* Vite's lib mode writes CSS to a separate file outside the Rollup
|
|
25
|
+
* bundle pipeline, so Rollup hooks like `generateBundle` never see it.
|
|
26
|
+
* This plugin uses `closeBundle` — which runs after all files are on
|
|
27
|
+
* disk — to find CSS files, inject their contents into the JS entry
|
|
28
|
+
* as a self-executing `<style>` injector, and delete the CSS files.
|
|
29
|
+
*/
|
|
30
|
+
export function sh3CssInline() {
|
|
31
|
+
let resolvedConfig;
|
|
32
|
+
return {
|
|
33
|
+
name: 'sh3-css-inline',
|
|
34
|
+
apply: 'build',
|
|
35
|
+
enforce: 'post',
|
|
36
|
+
configResolved(config) {
|
|
37
|
+
resolvedConfig = config;
|
|
38
|
+
},
|
|
39
|
+
closeBundle() {
|
|
40
|
+
const outDir = resolvedConfig.build.outDir;
|
|
41
|
+
// Find all .css and .js files in the output directory.
|
|
42
|
+
let files;
|
|
43
|
+
try {
|
|
44
|
+
files = readdirSync(outDir);
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const cssFiles = files.filter(f => f.endsWith('.css'));
|
|
50
|
+
const jsFiles = files.filter(f => f.endsWith('.js'));
|
|
51
|
+
if (cssFiles.length === 0 || jsFiles.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
// Read and concatenate all CSS.
|
|
54
|
+
const cssChunks = [];
|
|
55
|
+
for (const cssFile of cssFiles) {
|
|
56
|
+
const content = readFileSync(join(outDir, cssFile), 'utf-8').trim();
|
|
57
|
+
if (content)
|
|
58
|
+
cssChunks.push(content);
|
|
59
|
+
}
|
|
60
|
+
if (cssChunks.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
// Build a JS snippet that injects a <style> tag at load time.
|
|
63
|
+
const combined = cssChunks.join('\n');
|
|
64
|
+
const escaped = JSON.stringify(combined);
|
|
65
|
+
const injector = [
|
|
66
|
+
'/* sh3-css-inline: injected styles */',
|
|
67
|
+
'(function(){',
|
|
68
|
+
' const s=document.createElement("style");',
|
|
69
|
+
` s.textContent=${escaped};`,
|
|
70
|
+
' document.head.appendChild(s);',
|
|
71
|
+
'})();',
|
|
72
|
+
'',
|
|
73
|
+
].join('\n');
|
|
74
|
+
// Prepend the injector to the first JS file (the entry).
|
|
75
|
+
const jsEntry = jsFiles[0];
|
|
76
|
+
const jsPath = join(outDir, jsEntry);
|
|
77
|
+
const jsContent = readFileSync(jsPath, 'utf-8');
|
|
78
|
+
writeFileSync(jsPath, injector + jsContent);
|
|
79
|
+
// Delete the CSS files.
|
|
80
|
+
for (const cssFile of cssFiles) {
|
|
81
|
+
unlinkSync(join(outDir, cssFile));
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Vite plugin that produces a distributable artifact directory after build.
|
|
88
|
+
*
|
|
89
|
+
* After Vite's lib build completes, this plugin:
|
|
90
|
+
* 1. Finds the entry chunk and renames it to client.js
|
|
91
|
+
* 2. Optionally copies a pre-built server bundle as server.js
|
|
92
|
+
* 3. Extracts manifest fields (id, label, version, type) from the source
|
|
93
|
+
* 4. Writes manifest.json alongside the bundles
|
|
94
|
+
*/
|
|
95
|
+
export function sh3Artifact(options = {}) {
|
|
96
|
+
let outDir = '';
|
|
97
|
+
let entryFileName = '';
|
|
98
|
+
return {
|
|
99
|
+
name: 'sh3-artifact',
|
|
100
|
+
apply: 'build',
|
|
101
|
+
enforce: 'post',
|
|
102
|
+
configResolved(config) {
|
|
103
|
+
outDir = config.build.outDir;
|
|
104
|
+
},
|
|
105
|
+
generateBundle(_outputOptions, bundle) {
|
|
106
|
+
// Find the entry chunk — the one Rollup marks as isEntry.
|
|
107
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
108
|
+
if (chunk.type === 'chunk' && chunk.isEntry) {
|
|
109
|
+
entryFileName = fileName;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
async closeBundle() {
|
|
115
|
+
var _a, _b, _c, _d, _e;
|
|
116
|
+
if (!entryFileName)
|
|
117
|
+
return;
|
|
118
|
+
const clientSrc = join(outDir, entryFileName);
|
|
119
|
+
const clientDest = join(outDir, 'client.js');
|
|
120
|
+
// Read source before potentially overwriting.
|
|
121
|
+
let source = '';
|
|
122
|
+
try {
|
|
123
|
+
source = readFileSync(clientSrc, 'utf-8');
|
|
124
|
+
}
|
|
125
|
+
catch (_f) {
|
|
126
|
+
console.warn('[sh3-artifact] Could not read entry chunk:', clientSrc);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// Rename entry chunk to client.js (only if names differ).
|
|
130
|
+
if (clientSrc !== clientDest) {
|
|
131
|
+
writeFileSync(clientDest, source);
|
|
132
|
+
unlinkSync(clientSrc);
|
|
133
|
+
}
|
|
134
|
+
// --- Extract manifest fields from App or Shard block ---
|
|
135
|
+
//
|
|
136
|
+
// The bundle may contain both an App manifest (has `requiredShards`)
|
|
137
|
+
// and a Shard manifest (has `views: [`). We extract id/label/version
|
|
138
|
+
// from the App block first, then fall back to the Shard block.
|
|
139
|
+
// If neither block exists, the build fails with a clear error.
|
|
140
|
+
const hasApp = /\brequiredShards\s*:\s*\[/.test(source);
|
|
141
|
+
const hasShard = /\bviews\s*:\s*\[/.test(source);
|
|
142
|
+
if (!hasApp && !hasShard) {
|
|
143
|
+
throw new Error('[sh3-artifact] Could not find an App manifest (requiredShards) or Shard manifest (views) in the entry chunk. '
|
|
144
|
+
+ 'Ensure the entry exports an App or Shard object.');
|
|
145
|
+
}
|
|
146
|
+
const type = hasShard && hasApp ? 'combo' : hasApp ? 'app' : 'shard';
|
|
147
|
+
/**
|
|
148
|
+
* Extract id, label, and version from the manifest object block that
|
|
149
|
+
* contains `anchor`. Walks backwards from the anchor to find the
|
|
150
|
+
* opening `{`, then forward to find the matching `}`, and extracts
|
|
151
|
+
* fields from within that slice.
|
|
152
|
+
*/
|
|
153
|
+
function extractFromBlock(anchor) {
|
|
154
|
+
const anchorMatch = anchor.exec(source);
|
|
155
|
+
if (!anchorMatch)
|
|
156
|
+
return null;
|
|
157
|
+
// Walk backwards from the anchor to find the enclosing `{`.
|
|
158
|
+
let depth = 0;
|
|
159
|
+
let blockStart = anchorMatch.index;
|
|
160
|
+
for (let i = anchorMatch.index - 1; i >= 0; i--) {
|
|
161
|
+
if (source[i] === '}')
|
|
162
|
+
depth++;
|
|
163
|
+
if (source[i] === '{') {
|
|
164
|
+
if (depth === 0) {
|
|
165
|
+
blockStart = i;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
depth--;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Walk forwards from the anchor to find the matching `}`.
|
|
172
|
+
depth = 0;
|
|
173
|
+
let blockEnd = source.length;
|
|
174
|
+
for (let i = blockStart; i < source.length; i++) {
|
|
175
|
+
if (source[i] === '{')
|
|
176
|
+
depth++;
|
|
177
|
+
if (source[i] === '}') {
|
|
178
|
+
depth--;
|
|
179
|
+
if (depth === 0) {
|
|
180
|
+
blockEnd = i + 1;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const block = source.slice(blockStart, blockEnd);
|
|
186
|
+
const get = (pattern) => {
|
|
187
|
+
const m = block.match(pattern);
|
|
188
|
+
return m ? m[1] : '';
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
id: get(/\bid\s*:\s*["']([^"']+)["']/),
|
|
192
|
+
label: get(/\blabel\s*:\s*["']([^"']+)["']/),
|
|
193
|
+
version: get(/\bversion\s*:\s*["']([^"']+)["']/),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// App first, then Shard.
|
|
197
|
+
const extracted = (_a = extractFromBlock(/\brequiredShards\s*:\s*\[/)) !== null && _a !== void 0 ? _a : extractFromBlock(/\bviews\s*:\s*\[/);
|
|
198
|
+
const { id, label, version } = extracted;
|
|
199
|
+
// --- Optional server bundle ---
|
|
200
|
+
let hasServer = false;
|
|
201
|
+
if (options.serverEntry && existsSync(options.serverEntry)) {
|
|
202
|
+
copyFileSync(options.serverEntry, join(outDir, 'server.js'));
|
|
203
|
+
hasServer = true;
|
|
204
|
+
}
|
|
205
|
+
// --- Read fallback metadata from package.json ---
|
|
206
|
+
let pkgDescription;
|
|
207
|
+
let pkgAuthor;
|
|
208
|
+
try {
|
|
209
|
+
const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
|
|
210
|
+
pkgDescription = typeof pkg.description === 'string' ? pkg.description : undefined;
|
|
211
|
+
pkgAuthor = typeof pkg.author === 'string'
|
|
212
|
+
? pkg.author
|
|
213
|
+
: typeof ((_b = pkg.author) === null || _b === void 0 ? void 0 : _b.name) === 'string' ? pkg.author.name : undefined;
|
|
214
|
+
}
|
|
215
|
+
catch ( /* no package.json or unreadable */_g) { /* no package.json or unreadable */ }
|
|
216
|
+
// --- Write manifest.json ---
|
|
217
|
+
const overrides = (_c = options.manifest) !== null && _c !== void 0 ? _c : {};
|
|
218
|
+
const finalDescription = (_d = overrides.description) !== null && _d !== void 0 ? _d : pkgDescription;
|
|
219
|
+
const finalAuthor = (_e = overrides.author) !== null && _e !== void 0 ? _e : pkgAuthor;
|
|
220
|
+
if (!finalDescription) {
|
|
221
|
+
throw new Error('[sh3-artifact] Missing "description". Add it to package.json or pass it via sh3Artifact({ manifest: { description } }).');
|
|
222
|
+
}
|
|
223
|
+
if (!finalAuthor) {
|
|
224
|
+
throw new Error('[sh3-artifact] Missing "author". Add it to package.json or pass it via sh3Artifact({ manifest: { author } }).');
|
|
225
|
+
}
|
|
226
|
+
const manifest = Object.assign(Object.assign(Object.assign({ id: id || 'unknown', type, label: label || id || 'unknown', version: version || '0.0.0', contractVersion: 1, client: 'client.js' }, (hasServer ? { server: 'server.js' } : {})), { description: finalDescription, author: finalAuthor }), overrides);
|
|
227
|
+
writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
228
|
+
// --- Log summary ---
|
|
229
|
+
const files = ['manifest.json', 'client.js'];
|
|
230
|
+
if (hasServer)
|
|
231
|
+
files.push('server.js');
|
|
232
|
+
console.log(`[sh3-artifact] ${manifest.id}@${manifest.version} (${manifest.type}) → ${outDir}`);
|
|
233
|
+
console.log(`[sh3-artifact] files: ${files.join(', ')}`);
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const contract: {
|
|
2
|
+
readonly version: 1;
|
|
3
|
+
/** Import specifiers any shard/app source file may use. */
|
|
4
|
+
readonly shardImports: readonly ["sh3-core", "sh3-core/tokens.css"];
|
|
5
|
+
/** Import specifiers restricted to the host entry point only. */
|
|
6
|
+
readonly hostImports: readonly ["sh3-core/host"];
|
|
7
|
+
/** Contract-level prefix. Any import starting with 'sh3-core' that is not
|
|
8
|
+
* listed in shardImports or hostImports is illegal. */
|
|
9
|
+
readonly packagePrefix: "sh3-core";
|
|
10
|
+
readonly shard: {
|
|
11
|
+
readonly requiredFields: readonly ["id", "label", "version", "views"];
|
|
12
|
+
readonly views: {
|
|
13
|
+
readonly requiredFields: readonly ["id", "label"];
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
readonly app: {
|
|
17
|
+
readonly requiredFields: readonly ["id", "label", "version", "requiredShards", "layoutVersion"];
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type Contract = typeof contract;
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Machine-readable contract descriptor.
|
|
3
|
+
*
|
|
4
|
+
* Imported as `import { contract } from 'sh3-core/contract'`. The validator
|
|
5
|
+
* package reads this to know what import paths are legal, what fields
|
|
6
|
+
* manifests must have, and which contract version is in play.
|
|
7
|
+
*
|
|
8
|
+
* Pure data — no runtime logic, no dependencies.
|
|
9
|
+
*/
|
|
10
|
+
export const contract = {
|
|
11
|
+
version: 1,
|
|
12
|
+
/** Import specifiers any shard/app source file may use. */
|
|
13
|
+
shardImports: ['sh3-core', 'sh3-core/tokens.css'],
|
|
14
|
+
/** Import specifiers restricted to the host entry point only. */
|
|
15
|
+
hostImports: ['sh3-core/host'],
|
|
16
|
+
/** Contract-level prefix. Any import starting with 'sh3-core' that is not
|
|
17
|
+
* listed in shardImports or hostImports is illegal. */
|
|
18
|
+
packagePrefix: 'sh3-core',
|
|
19
|
+
shard: {
|
|
20
|
+
requiredFields: ['id', 'label', 'version', 'views'],
|
|
21
|
+
views: {
|
|
22
|
+
requiredFields: ['id', 'label'],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
app: {
|
|
26
|
+
requiredFields: ['id', 'label', 'version', 'requiredShards', 'layoutVersion'],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Shard, App } from './index';
|
|
2
|
+
export interface ShellConfig {
|
|
3
|
+
/** Framework shard IDs to exclude (all included by default) */
|
|
4
|
+
excludeShards?: string[];
|
|
5
|
+
/** Additional shards to register */
|
|
6
|
+
shards?: Shard[];
|
|
7
|
+
/** Additional apps to register */
|
|
8
|
+
apps?: App[];
|
|
9
|
+
/**
|
|
10
|
+
* Packages discovered by the server at boot.
|
|
11
|
+
* The frontend fetches this list from /api/packages and passes it here.
|
|
12
|
+
*/
|
|
13
|
+
discoveredPackages?: Array<{
|
|
14
|
+
id: string;
|
|
15
|
+
type: string;
|
|
16
|
+
version: string;
|
|
17
|
+
bundleUrl: string;
|
|
18
|
+
}>;
|
|
19
|
+
/** Mount target — CSS selector or element (defaults to '#app') */
|
|
20
|
+
target?: string | HTMLElement;
|
|
21
|
+
/** Server base URL ('' for same-origin) */
|
|
22
|
+
serverUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function createShell(config?: ShellConfig): Promise<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* createShell — public factory for booting an SH3 shell.
|
|
3
|
+
*
|
|
4
|
+
* Consumers call this from their own main.ts instead of manually
|
|
5
|
+
* importing registerShard / registerApp / bootstrap / Shell. The
|
|
6
|
+
* factory handles platform detection, boot config, auth gating,
|
|
7
|
+
* registration, bootstrap, and mounting in the correct order.
|
|
8
|
+
*/
|
|
9
|
+
import { mount, unmount } from 'svelte';
|
|
10
|
+
import { Shell } from './index';
|
|
11
|
+
import { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner, } from './host';
|
|
12
|
+
import { resolvePlatform } from './platform/index';
|
|
13
|
+
import { hydrateTokenOverrides } from './theme';
|
|
14
|
+
import { __setEnvServerUrl } from './env/index';
|
|
15
|
+
import { __setTenantId } from './documents/config';
|
|
16
|
+
import { initFromBoot } from './auth/index';
|
|
17
|
+
import SignInWall from './auth/SignInWall.svelte';
|
|
18
|
+
export async function createShell(config) {
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
const sUrl = (_a = config === null || config === void 0 ? void 0 : config.serverUrl) !== null && _a !== void 0 ? _a : '';
|
|
21
|
+
// 1. Platform detection
|
|
22
|
+
const platform = await resolvePlatform();
|
|
23
|
+
if (platform.backends) {
|
|
24
|
+
__setBackend('workspace', platform.backends.workspace);
|
|
25
|
+
__setBackend('user', platform.backends.user);
|
|
26
|
+
}
|
|
27
|
+
if (platform.localOwner) {
|
|
28
|
+
setLocalOwner();
|
|
29
|
+
}
|
|
30
|
+
__setEnvServerUrl(sUrl);
|
|
31
|
+
hydrateTokenOverrides();
|
|
32
|
+
// 2. Resolve mount target early (needed for both sign-in wall and shell)
|
|
33
|
+
const target = typeof (config === null || config === void 0 ? void 0 : config.target) === 'string'
|
|
34
|
+
? document.querySelector(config.target)
|
|
35
|
+
: (_b = config === null || config === void 0 ? void 0 : config.target) !== null && _b !== void 0 ? _b : document.getElementById('app');
|
|
36
|
+
if (!target) {
|
|
37
|
+
throw new Error('SH3: mount target not found');
|
|
38
|
+
}
|
|
39
|
+
// 3. Fetch boot config (skip for local-owner platforms like Tauri/dev)
|
|
40
|
+
let bootConfig = null;
|
|
41
|
+
if (!platform.localOwner) {
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(`${sUrl}/api/boot`, { credentials: 'include' });
|
|
44
|
+
if (res.ok) {
|
|
45
|
+
bootConfig = await res.json();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (_d) {
|
|
49
|
+
// Server unreachable — boot without auth (offline mode)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 4. Auth decision point
|
|
53
|
+
if (platform.localOwner) {
|
|
54
|
+
// Local-owner (Tauri/dev): no auth, no sign-in, tenant is 'local'.
|
|
55
|
+
// setLocalOwner() already called above — admin is assumed.
|
|
56
|
+
__setTenantId('local');
|
|
57
|
+
}
|
|
58
|
+
else if (bootConfig) {
|
|
59
|
+
initFromBoot(sUrl, bootConfig);
|
|
60
|
+
__setTenantId(bootConfig.tenantId);
|
|
61
|
+
const { auth, session } = bootConfig;
|
|
62
|
+
// Hard gate: no session, auth required, no guest allowed → sign-in wall
|
|
63
|
+
if (!session && auth.required && !auth.guestAllowed) {
|
|
64
|
+
await showSignInWall(target, bootConfig);
|
|
65
|
+
// After successful sign-in, re-fetch boot config
|
|
66
|
+
const res = await fetch(`${sUrl}/api/boot`, { credentials: 'include' });
|
|
67
|
+
if (res.ok) {
|
|
68
|
+
bootConfig = await res.json();
|
|
69
|
+
initFromBoot(sUrl, bootConfig);
|
|
70
|
+
__setTenantId(bootConfig.tenantId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// 5. Load server-discovered packages
|
|
75
|
+
if ((_c = config === null || config === void 0 ? void 0 : config.discoveredPackages) === null || _c === void 0 ? void 0 : _c.length) {
|
|
76
|
+
const { loadBundleModule } = await import('./registry/loader');
|
|
77
|
+
for (const pkg of config.discoveredPackages) {
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(pkg.bundleUrl);
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
console.warn(`[sh3] Failed to fetch discovered package "${pkg.id}": HTTP ${res.status}`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const bytes = await res.arrayBuffer();
|
|
85
|
+
const loaded = await loadBundleModule(bytes);
|
|
86
|
+
for (const s of loaded.shards)
|
|
87
|
+
registerShard(s);
|
|
88
|
+
for (const a of loaded.apps)
|
|
89
|
+
registerApp(a);
|
|
90
|
+
console.log(`[sh3] Loaded discovered package: ${pkg.id}`);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.warn(`[sh3] Failed to load discovered package "${pkg.id}":`, err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 6. Register consumer-provided shards and apps
|
|
98
|
+
if (config === null || config === void 0 ? void 0 : config.shards) {
|
|
99
|
+
for (const shard of config.shards)
|
|
100
|
+
registerShard(shard);
|
|
101
|
+
}
|
|
102
|
+
if (config === null || config === void 0 ? void 0 : config.apps) {
|
|
103
|
+
for (const app of config.apps)
|
|
104
|
+
registerApp(app);
|
|
105
|
+
}
|
|
106
|
+
// 7. Bootstrap
|
|
107
|
+
const bootstrapConfig = {};
|
|
108
|
+
if (config === null || config === void 0 ? void 0 : config.excludeShards)
|
|
109
|
+
bootstrapConfig.excludeShards = config.excludeShards;
|
|
110
|
+
await bootstrap(bootstrapConfig);
|
|
111
|
+
// 8. Mount the shell
|
|
112
|
+
mount(Shell, { target });
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Show the sign-in wall and wait until the user authenticates.
|
|
116
|
+
* Returns a promise that resolves after successful login.
|
|
117
|
+
*/
|
|
118
|
+
function showSignInWall(target, bootConfig) {
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
const instance = mount(SignInWall, {
|
|
121
|
+
target,
|
|
122
|
+
props: {
|
|
123
|
+
selfRegistration: bootConfig.auth.selfRegistration,
|
|
124
|
+
onSuccess: () => {
|
|
125
|
+
unmount(instance);
|
|
126
|
+
resolve();
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DocumentBackend, DocumentMeta } from './types';
|
|
2
|
+
export declare class MemoryDocumentBackend implements DocumentBackend {
|
|
3
|
+
#private;
|
|
4
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
5
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
6
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
7
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
8
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
export declare class IndexedDBDocumentBackend implements DocumentBackend {
|
|
11
|
+
#private;
|
|
12
|
+
read(tenantId: string, shardId: string, path: string): Promise<string | ArrayBuffer | null>;
|
|
13
|
+
write(tenantId: string, shardId: string, path: string, content: string | ArrayBuffer): Promise<void>;
|
|
14
|
+
delete(tenantId: string, shardId: string, path: string): Promise<void>;
|
|
15
|
+
list(tenantId: string, shardId: string): Promise<DocumentMeta[]>;
|
|
16
|
+
exists(tenantId: string, shardId: string, path: string): Promise<boolean>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Document zone backends — concrete storage implementations.
|
|
3
|
+
*
|
|
4
|
+
* MemoryDocumentBackend: Map-based, for tests and ephemeral use.
|
|
5
|
+
* IndexedDBDocumentBackend: The web default. Lazy-inits on first
|
|
6
|
+
* operation to avoid blocking bootstrap.
|
|
7
|
+
*/
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
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");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
14
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
15
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
16
|
+
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");
|
|
17
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
18
|
+
};
|
|
19
|
+
var _MemoryDocumentBackend_store, _IndexedDBDocumentBackend_instances, _IndexedDBDocumentBackend_dbPromise, _IndexedDBDocumentBackend_db, _IndexedDBDocumentBackend_tx;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function compositeKey(tenantId, shardId, path) {
|
|
24
|
+
return `${tenantId}/${shardId}/${path}`;
|
|
25
|
+
}
|
|
26
|
+
function keyPrefix(tenantId, shardId) {
|
|
27
|
+
return `${tenantId}/${shardId}/`;
|
|
28
|
+
}
|
|
29
|
+
export class MemoryDocumentBackend {
|
|
30
|
+
constructor() {
|
|
31
|
+
_MemoryDocumentBackend_store.set(this, new Map());
|
|
32
|
+
}
|
|
33
|
+
async read(tenantId, shardId, path) {
|
|
34
|
+
const entry = __classPrivateFieldGet(this, _MemoryDocumentBackend_store, "f").get(compositeKey(tenantId, shardId, path));
|
|
35
|
+
return entry ? entry.content : null;
|
|
36
|
+
}
|
|
37
|
+
async write(tenantId, shardId, path, content) {
|
|
38
|
+
const size = typeof content === 'string' ? new Blob([content]).size : content.byteLength;
|
|
39
|
+
__classPrivateFieldGet(this, _MemoryDocumentBackend_store, "f").set(compositeKey(tenantId, shardId, path), {
|
|
40
|
+
content,
|
|
41
|
+
size,
|
|
42
|
+
lastModified: Date.now(),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async delete(tenantId, shardId, path) {
|
|
46
|
+
__classPrivateFieldGet(this, _MemoryDocumentBackend_store, "f").delete(compositeKey(tenantId, shardId, path));
|
|
47
|
+
}
|
|
48
|
+
async list(tenantId, shardId) {
|
|
49
|
+
const prefix = keyPrefix(tenantId, shardId);
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const [key, entry] of __classPrivateFieldGet(this, _MemoryDocumentBackend_store, "f")) {
|
|
52
|
+
if (key.startsWith(prefix)) {
|
|
53
|
+
out.push({
|
|
54
|
+
path: key.slice(prefix.length),
|
|
55
|
+
size: entry.size,
|
|
56
|
+
lastModified: entry.lastModified,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
async exists(tenantId, shardId, path) {
|
|
63
|
+
return __classPrivateFieldGet(this, _MemoryDocumentBackend_store, "f").has(compositeKey(tenantId, shardId, path));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
_MemoryDocumentBackend_store = new WeakMap();
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// IndexedDBDocumentBackend
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
const IDB_NAME = 'sh3-documents';
|
|
71
|
+
const IDB_STORE = 'docs';
|
|
72
|
+
const IDB_VERSION = 2;
|
|
73
|
+
export class IndexedDBDocumentBackend {
|
|
74
|
+
constructor() {
|
|
75
|
+
_IndexedDBDocumentBackend_instances.add(this);
|
|
76
|
+
_IndexedDBDocumentBackend_dbPromise.set(this, null);
|
|
77
|
+
}
|
|
78
|
+
async read(tenantId, shardId, path) {
|
|
79
|
+
const key = compositeKey(tenantId, shardId, path);
|
|
80
|
+
const entry = await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_tx).call(this, 'readonly', (s) => s.get(key));
|
|
81
|
+
return entry ? entry.content : null;
|
|
82
|
+
}
|
|
83
|
+
async write(tenantId, shardId, path, content) {
|
|
84
|
+
const key = compositeKey(tenantId, shardId, path);
|
|
85
|
+
const size = typeof content === 'string' ? new Blob([content]).size : content.byteLength;
|
|
86
|
+
const entry = { content, size, lastModified: Date.now() };
|
|
87
|
+
await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_tx).call(this, 'readwrite', (s) => s.put(entry, key));
|
|
88
|
+
}
|
|
89
|
+
async delete(tenantId, shardId, path) {
|
|
90
|
+
const key = compositeKey(tenantId, shardId, path);
|
|
91
|
+
await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_tx).call(this, 'readwrite', (s) => s.delete(key));
|
|
92
|
+
}
|
|
93
|
+
async list(tenantId, shardId) {
|
|
94
|
+
const prefix = keyPrefix(tenantId, shardId);
|
|
95
|
+
const db = await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_db).call(this);
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const tx = db.transaction(IDB_STORE, 'readonly');
|
|
98
|
+
const store = tx.objectStore(IDB_STORE);
|
|
99
|
+
// IDBKeyRange.bound selects all keys that start with the prefix.
|
|
100
|
+
// The upper bound appends a character beyond '/' to capture all
|
|
101
|
+
// sub-paths without over-selecting.
|
|
102
|
+
const range = IDBKeyRange.bound(prefix, prefix + '\uffff', false, false);
|
|
103
|
+
const req = store.openCursor(range);
|
|
104
|
+
const out = [];
|
|
105
|
+
req.onsuccess = () => {
|
|
106
|
+
const cursor = req.result;
|
|
107
|
+
if (cursor) {
|
|
108
|
+
const entry = cursor.value;
|
|
109
|
+
out.push({
|
|
110
|
+
path: cursor.key.slice(prefix.length),
|
|
111
|
+
size: entry.size,
|
|
112
|
+
lastModified: entry.lastModified,
|
|
113
|
+
});
|
|
114
|
+
cursor.continue();
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
resolve(out);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
req.onerror = () => reject(req.error);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async exists(tenantId, shardId, path) {
|
|
124
|
+
const key = compositeKey(tenantId, shardId, path);
|
|
125
|
+
// getKey is cheaper than get — avoids deserializing the value.
|
|
126
|
+
const result = await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_tx).call(this, 'readonly', (s) => s.getKey(key));
|
|
127
|
+
return result !== undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
_IndexedDBDocumentBackend_dbPromise = new WeakMap(), _IndexedDBDocumentBackend_instances = new WeakSet(), _IndexedDBDocumentBackend_db = function _IndexedDBDocumentBackend_db() {
|
|
131
|
+
if (!__classPrivateFieldGet(this, _IndexedDBDocumentBackend_dbPromise, "f")) {
|
|
132
|
+
__classPrivateFieldSet(this, _IndexedDBDocumentBackend_dbPromise, new Promise((resolve, reject) => {
|
|
133
|
+
const req = indexedDB.open(IDB_NAME, IDB_VERSION);
|
|
134
|
+
req.onupgradeneeded = () => {
|
|
135
|
+
const db = req.result;
|
|
136
|
+
if (!db.objectStoreNames.contains(IDB_STORE)) {
|
|
137
|
+
db.createObjectStore(IDB_STORE);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
req.onsuccess = () => resolve(req.result);
|
|
141
|
+
req.onerror = () => reject(req.error);
|
|
142
|
+
}), "f");
|
|
143
|
+
}
|
|
144
|
+
return __classPrivateFieldGet(this, _IndexedDBDocumentBackend_dbPromise, "f");
|
|
145
|
+
}, _IndexedDBDocumentBackend_tx =
|
|
146
|
+
/** Run a single-store transaction and return the request result. */
|
|
147
|
+
async function _IndexedDBDocumentBackend_tx(mode, fn) {
|
|
148
|
+
const db = await __classPrivateFieldGet(this, _IndexedDBDocumentBackend_instances, "m", _IndexedDBDocumentBackend_db).call(this);
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const tx = db.transaction(IDB_STORE, mode);
|
|
151
|
+
const store = tx.objectStore(IDB_STORE);
|
|
152
|
+
const req = fn(store);
|
|
153
|
+
req.onsuccess = () => resolve(req.result);
|
|
154
|
+
req.onerror = () => reject(req.error);
|
|
155
|
+
});
|
|
156
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DocumentBackend } from './types';
|
|
2
|
+
export declare function getTenantId(): string;
|
|
3
|
+
export declare function getDocumentBackend(): DocumentBackend;
|
|
4
|
+
/** Host-only. Set the tenant id before bootstrap(). */
|
|
5
|
+
export declare function __setTenantId(id: string): void;
|
|
6
|
+
/** Host-only. Swap the document backend before bootstrap(). */
|
|
7
|
+
export declare function __setDocumentBackend(b: DocumentBackend): void;
|