@lokvis/runtime 0.1.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 (61) hide show
  1. package/dist/asset-store.d.ts +72 -0
  2. package/dist/asset-store.d.ts.map +1 -0
  3. package/dist/asset-store.js +167 -0
  4. package/dist/asset-store.js.map +1 -0
  5. package/dist/capability-registry.d.ts +38 -0
  6. package/dist/capability-registry.d.ts.map +1 -0
  7. package/dist/capability-registry.js +100 -0
  8. package/dist/capability-registry.js.map +1 -0
  9. package/dist/event-bus.d.ts +9 -0
  10. package/dist/event-bus.d.ts.map +1 -0
  11. package/dist/event-bus.js +33 -0
  12. package/dist/event-bus.js.map +1 -0
  13. package/dist/executor.d.ts +42 -0
  14. package/dist/executor.d.ts.map +1 -0
  15. package/dist/executor.js +237 -0
  16. package/dist/executor.js.map +1 -0
  17. package/dist/history.d.ts +92 -0
  18. package/dist/history.d.ts.map +1 -0
  19. package/dist/history.js +171 -0
  20. package/dist/history.js.map +1 -0
  21. package/dist/idb-asset-store.d.ts +46 -0
  22. package/dist/idb-asset-store.d.ts.map +1 -0
  23. package/dist/idb-asset-store.js +76 -0
  24. package/dist/idb-asset-store.js.map +1 -0
  25. package/dist/index.d.ts +19 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +19 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/opfs-asset-store.d.ts +38 -0
  30. package/dist/opfs-asset-store.d.ts.map +1 -0
  31. package/dist/opfs-asset-store.js +122 -0
  32. package/dist/opfs-asset-store.js.map +1 -0
  33. package/dist/runtime.d.ts +87 -0
  34. package/dist/runtime.d.ts.map +1 -0
  35. package/dist/runtime.js +475 -0
  36. package/dist/runtime.js.map +1 -0
  37. package/dist/types.d.ts +99 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/dist/types.js +8 -0
  40. package/dist/types.js.map +1 -0
  41. package/dist/worker-host.d.ts +154 -0
  42. package/dist/worker-host.d.ts.map +1 -0
  43. package/dist/worker-host.js +394 -0
  44. package/dist/worker-host.js.map +1 -0
  45. package/dist/worker-protocol.d.ts +103 -0
  46. package/dist/worker-protocol.d.ts.map +1 -0
  47. package/dist/worker-protocol.js +87 -0
  48. package/dist/worker-protocol.js.map +1 -0
  49. package/package.json +49 -0
  50. package/src/asset-store.ts +222 -0
  51. package/src/capability-registry.ts +135 -0
  52. package/src/event-bus.ts +41 -0
  53. package/src/executor.ts +303 -0
  54. package/src/history.ts +229 -0
  55. package/src/idb-asset-store.ts +121 -0
  56. package/src/index.ts +20 -0
  57. package/src/opfs-asset-store.ts +167 -0
  58. package/src/runtime.ts +564 -0
  59. package/src/types.ts +121 -0
  60. package/src/worker-host.ts +531 -0
  61. package/src/worker-protocol.ts +187 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * OPFS Asset Store
3
+ *
4
+ * 大文件(blob)存 OPFS(Origin Private File System),元数据存内存 Map。
5
+ * OPFS 是浏览器私有文件系统,blob 持久化(刷新后文件仍在),但元数据仅在内存 ——
6
+ * 刷新后 list() 会返回空、get(id) 返回 undefined(除非元数据由上层持久化)。
7
+ * 完整元数据持久化建议叠加 IdbAssetStore 或在 W3 补强。
8
+ *
9
+ * 注:任务规格提及 FileSystemSyncAccessHandle(Worker 内同步句柄),
10
+ * 但 AssetStore 接口本身为 async,主线程仅可使用 FileSystemFileHandle 异步 API,
11
+ * 因此本实现采用异步文件句柄,语义等价且兼容主线程与 Worker。
12
+ *
13
+ * 降级链(W2.8 工厂):OPFS → IndexedDB → Memory
14
+ */
15
+
16
+ import type { Asset, AssetId } from '@lokvis/schema';
17
+ import type { AssetStore } from './asset-store.js';
18
+ import {
19
+ buildAsset,
20
+ generateId,
21
+ parseBlobPath,
22
+ prepareImport,
23
+ } from './asset-store.js';
24
+
25
+ /** OPFS 不可用或初始化失败时抛出 */
26
+ export class OpfsUnavailableError extends Error {
27
+ constructor(message: string) {
28
+ super(message);
29
+ this.name = 'OpfsUnavailableError';
30
+ }
31
+ }
32
+
33
+ /** OPFS 文件存储路径前缀(出现在 BlobHandle.path 中) */
34
+ export const OPFS_PATH_PREFIX = 'opfs';
35
+
36
+ /** OPFS 文件名后缀 */
37
+ const OPFS_FILE_SUFFIX = '.bin';
38
+
39
+ /** OPFS AssetStore 配置 */
40
+ export interface OpfsAssetStoreOptions {
41
+ /** OPFS 根目录下的命名空间目录名(默认 'lokvis') */
42
+ rootDirName?: string;
43
+ /** 资产子目录名(默认 'assets') */
44
+ assetsDirName?: string;
45
+ /**
46
+ * 测试注入:自定义 OPFS 根目录句柄。
47
+ * 默认使用 navigator.storage.getDirectory()。
48
+ */
49
+ rootHandle?: FileSystemDirectoryHandle;
50
+ }
51
+
52
+ /** 检测当前环境是否支持 OPFS */
53
+ export function isOpfsSupported(): boolean {
54
+ return (
55
+ typeof navigator !== 'undefined' &&
56
+ typeof navigator.storage !== 'undefined' &&
57
+ typeof navigator.storage.getDirectory === 'function'
58
+ );
59
+ }
60
+
61
+ /** 获取 OPFS 根目录句柄(自动创建命名空间目录) */
62
+ async function resolveAssetsDir(
63
+ options: OpfsAssetStoreOptions
64
+ ): Promise<FileSystemDirectoryHandle> {
65
+ let root: FileSystemDirectoryHandle;
66
+ if (options.rootHandle) {
67
+ root = options.rootHandle;
68
+ } else {
69
+ if (!isOpfsSupported()) {
70
+ throw new OpfsUnavailableError(
71
+ 'OPFS is not available: navigator.storage.getDirectory is undefined'
72
+ );
73
+ }
74
+ root = await navigator.storage.getDirectory();
75
+ }
76
+ const lokvisDir = await root.getDirectoryHandle(
77
+ options.rootDirName ?? 'lokvis',
78
+ { create: true }
79
+ );
80
+ return lokvisDir.getDirectoryHandle(options.assetsDirName ?? 'assets', {
81
+ create: true,
82
+ });
83
+ }
84
+
85
+ /** 将 Blob 写入 OPFS 文件 */
86
+ async function writeOpfsFile(
87
+ dir: FileSystemDirectoryHandle,
88
+ fileName: string,
89
+ blob: Blob
90
+ ): Promise<FileSystemFileHandle> {
91
+ const fileHandle = await dir.getFileHandle(fileName, { create: true });
92
+ const writable = await fileHandle.createWritable();
93
+ try {
94
+ await writable.write(blob);
95
+ } finally {
96
+ await writable.close();
97
+ }
98
+ return fileHandle;
99
+ }
100
+
101
+ /** 创建 OPFS 版 AssetStore */
102
+ export async function createOpfsAssetStore(
103
+ options: OpfsAssetStoreOptions = {}
104
+ ): Promise<AssetStore> {
105
+ const assetsDir = await resolveAssetsDir(options);
106
+ /** 元数据缓存(内存中;持久化由 IdbAssetStore 负责) */
107
+ const assets = new Map<AssetId, Asset>();
108
+ /** id → 文件句柄(用于 getBlob/remove) */
109
+ const fileHandles = new Map<AssetId, FileSystemFileHandle>();
110
+
111
+ /** 从 id 派生 OPFS 文件名 */
112
+ const fileName = (id: AssetId) => `${id}${OPFS_FILE_SUFFIX}`;
113
+
114
+ return {
115
+ async import(source) {
116
+ const { id, blob, metadata, type } = prepareImport(source);
117
+ const handle = await writeOpfsFile(assetsDir, fileName(id), blob);
118
+ fileHandles.set(id, handle);
119
+ const asset = buildAsset(id, blob, metadata, type, OPFS_PATH_PREFIX);
120
+ assets.set(id, asset);
121
+ return asset;
122
+ },
123
+
124
+ async get(id) {
125
+ return assets.get(id);
126
+ },
127
+
128
+ async getBlob(handle) {
129
+ const id = parseBlobPath(handle.path, OPFS_PATH_PREFIX);
130
+ let fileHandle = fileHandles.get(id);
131
+ if (!fileHandle) {
132
+ // 可能是进程重启后元数据丢失但句柄仍可恢复
133
+ try {
134
+ fileHandle = await assetsDir.getFileHandle(fileName(id));
135
+ fileHandles.set(id, fileHandle);
136
+ } catch {
137
+ throw new Error(`Blob not found in OPFS for path: ${handle.path}`);
138
+ }
139
+ }
140
+ const file = await fileHandle.getFile();
141
+ return file;
142
+ },
143
+
144
+ async remove(id) {
145
+ assets.delete(id);
146
+ fileHandles.delete(id);
147
+ try {
148
+ await assetsDir.removeEntry(fileName(id));
149
+ } catch {
150
+ // 文件不存在视为已删除,静默忽略
151
+ }
152
+ },
153
+
154
+ async list() {
155
+ return Array.from(assets.values());
156
+ },
157
+
158
+ async create(blob, metadata, type) {
159
+ const id = generateId();
160
+ const handle = await writeOpfsFile(assetsDir, fileName(id), blob);
161
+ fileHandles.set(id, handle);
162
+ const asset = buildAsset(id, blob, metadata, type, OPFS_PATH_PREFIX);
163
+ assets.set(id, asset);
164
+ return asset;
165
+ },
166
+ };
167
+ }