@uns-kit/core 3.0.44 → 3.0.45

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.
@@ -0,0 +1,373 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ import { available, maintenanceLease, runtimeLease } from "./retained-store-storage.mjs";
6
+ const segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/);
7
+ const relative = z
8
+ .string()
9
+ .min(1)
10
+ .max(256)
11
+ .refine((value) => !path.isAbsolute(value) && !value.includes("\\") && value.split("/").every((p) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(p)));
12
+ export const RETAINED_STORE_MANIFEST = "retained-store.manifest.json";
13
+ export const RETAINED_STORE_BINDING = "retained-store-binding.json";
14
+ export const RETAINED_STORE_GUARD = "retained-store-guard.json";
15
+ export const retainedStoreManifestSchema = z
16
+ .object({
17
+ protocol: z.literal(1),
18
+ storageProtocol: z.literal(1).optional(),
19
+ format: segment,
20
+ resources: z
21
+ .array(z
22
+ .object({
23
+ id: segment,
24
+ path: relative,
25
+ kind: z.enum(["file", "directory"]),
26
+ required: z.boolean(),
27
+ backup: z.enum(["payload", "metadata"]).optional(),
28
+ })
29
+ .strict())
30
+ .min(1)
31
+ .max(16),
32
+ legacyVersions: z.array(segment).max(32).default([]),
33
+ })
34
+ .strict()
35
+ .superRefine((m, ctx) => {
36
+ if (new Set(m.resources.map((r) => r.id)).size !== m.resources.length)
37
+ ctx.addIssue({ code: "custom", message: "Duplicate resource ID" });
38
+ for (const r of m.resources) {
39
+ if (["store.json", "store-resources.json", "store-operation.json", "store-lease.sqlite", "store-returned.json"].includes(r.id))
40
+ ctx.addIssue({ code: "custom", message: "Reserved store metadata resource" });
41
+ if (["config.json", "controller-state.json", RETAINED_STORE_BINDING, RETAINED_STORE_GUARD].includes(r.path))
42
+ ctx.addIssue({ code: "custom", message: "Reserved instance resource" });
43
+ if (m.resources.some((o) => o !== r && (o.path === r.path || o.path.startsWith(r.path + "/"))))
44
+ ctx.addIssue({ code: "custom", message: "Overlapping resources" });
45
+ }
46
+ });
47
+ const ownerSchema = z.object({ rttNode: segment, version: segment, instanceId: segment }).strict();
48
+ const bindingSchema = z
49
+ .object({
50
+ protocol: z.literal(1),
51
+ storeId: z.uuid(),
52
+ generation: z.uuid(),
53
+ operationId: z.uuid(),
54
+ directory: z.string().max(4096),
55
+ format: segment,
56
+ configurationDigest: z.string().regex(/^[a-f0-9]{64}$/),
57
+ owner: ownerSchema,
58
+ maintenance: z.literal(true),
59
+ })
60
+ .strict();
61
+ export function retainedStoreDigest(value) {
62
+ const stable = (v) => Array.isArray(v)
63
+ ? v.map(stable)
64
+ : v && typeof v === "object"
65
+ ? Object.fromEntries(Object.keys(v)
66
+ .sort()
67
+ .map((k) => [k, stable(v[k])]))
68
+ : v;
69
+ return createHash("sha256")
70
+ .update(JSON.stringify(stable(value)))
71
+ .digest("hex");
72
+ }
73
+ export function retainedStoreEntry(file) {
74
+ try {
75
+ return fs.lstatSync(file);
76
+ }
77
+ catch (e) {
78
+ if (e.code === "ENOENT")
79
+ return;
80
+ throw e;
81
+ }
82
+ }
83
+ export function assertRetainedStorePath(file) {
84
+ if (!path.isAbsolute(file) || path.normalize(file) !== file)
85
+ throw Error("Unnormalized retained store path");
86
+ for (let p = file; p !== path.dirname(p); p = path.dirname(p)) {
87
+ const s = retainedStoreEntry(p);
88
+ if (s?.isSymbolicLink())
89
+ throw Error("Linked retained store path");
90
+ }
91
+ }
92
+ export function readRetainedStoreJson(file) {
93
+ assertRetainedStorePath(file);
94
+ const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
95
+ try {
96
+ const s = fs.fstatSync(fd);
97
+ if (!s.isFile() || s.nlink !== 1 || s.size > 128 * 1024 || s.uid !== process.getuid?.())
98
+ throw Error("Unsafe retained store metadata");
99
+ return JSON.parse(fs.readFileSync(fd, "utf8"));
100
+ }
101
+ finally {
102
+ fs.closeSync(fd);
103
+ }
104
+ }
105
+ export function syncRetainedDirectory(directory) {
106
+ const fd = fs.openSync(directory, "r");
107
+ try {
108
+ fs.fsyncSync(fd);
109
+ }
110
+ finally {
111
+ fs.closeSync(fd);
112
+ }
113
+ }
114
+ export function writeRetainedStoreJson(file, value) {
115
+ assertRetainedStorePath(file);
116
+ const temporary = file + "." + randomUUID() + ".tmp";
117
+ const text = JSON.stringify(value);
118
+ if (Buffer.byteLength(text) > 128 * 1024)
119
+ throw Error("Retained store metadata too large");
120
+ try {
121
+ const fd = fs.openSync(temporary, "wx", 0o600);
122
+ try {
123
+ fs.writeFileSync(fd, text);
124
+ fs.fsyncSync(fd);
125
+ }
126
+ finally {
127
+ fs.closeSync(fd);
128
+ }
129
+ fs.renameSync(temporary, file);
130
+ syncRetainedDirectory(path.dirname(file));
131
+ }
132
+ finally {
133
+ if (retainedStoreEntry(temporary))
134
+ fs.unlinkSync(temporary);
135
+ }
136
+ }
137
+ export function readRetainedStoreManifest(packageDirectory) {
138
+ return retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(packageDirectory, RETAINED_STORE_MANIFEST)));
139
+ }
140
+ export function prepareRetainedStoreMove(source, root, storeId, manifest) {
141
+ z.uuid().parse(storeId);
142
+ retainedStoreManifestSchema.parse(manifest);
143
+ assertRetainedStorePath(source);
144
+ assertRetainedStorePath(root);
145
+ const directory = path.join(root, storeId);
146
+ if (retainedStoreEntry(directory))
147
+ throw Error("Retained store destination exists");
148
+ const existingRoot = retainedStoreEntry(root);
149
+ if (existingRoot && (!existingRoot.isDirectory() || existingRoot.uid !== process.getuid?.() || existingRoot.mode & 0o077))
150
+ throw Error("Retained root must be private and owned");
151
+ const rootStat = existingRoot ?? fs.statSync(path.dirname(root)), sourceStat = fs.statSync(source);
152
+ if (root === source || root.startsWith(source + path.sep) || source.startsWith(root + path.sep))
153
+ throw Error("Nested retained store roots");
154
+ if (rootStat.dev !== sourceStat.dev)
155
+ throw Error("Retained store requires same filesystem; copy is not permitted");
156
+ const moves = [];
157
+ for (const resource of manifest.resources) {
158
+ const file = path.join(source, resource.path);
159
+ assertRetainedStorePath(file);
160
+ const s = retainedStoreEntry(file);
161
+ if (!s) {
162
+ if (resource.required)
163
+ throw Error("Required retained resource missing");
164
+ continue;
165
+ }
166
+ if (s.uid !== process.getuid?.() ||
167
+ s.dev !== rootStat.dev ||
168
+ s.isSymbolicLink() ||
169
+ (resource.kind === "file" ? !s.isFile() || s.nlink !== 1 : !s.isDirectory()))
170
+ throw Error("Unsafe retained resource");
171
+ moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino });
172
+ }
173
+ return { storeId, generation: randomUUID(), format: manifest.format, source, directory, slots: manifest.resources, moves };
174
+ }
175
+ function matches(file, move) {
176
+ assertRetainedStorePath(file);
177
+ const s = retainedStoreEntry(file);
178
+ return (!!s &&
179
+ !s.isSymbolicLink() &&
180
+ s.dev === move.device &&
181
+ s.ino === move.inode &&
182
+ s.uid === process.getuid?.() &&
183
+ (move.kind === "file" ? s.isFile() && s.nlink === 1 : s.isDirectory()));
184
+ }
185
+ /** Caller durably records the complete plan and holds its resource reservation first.
186
+ * Each rename is atomic; the multi-resource operation is deliberately resumable. */
187
+ export function applyRetainedStoreMove(plan, direction, afterMove) {
188
+ z.object({
189
+ storeId: z.uuid(),
190
+ generation: z.uuid(),
191
+ format: segment,
192
+ source: z.string().max(4096),
193
+ directory: z.string().max(4096),
194
+ slots: z
195
+ .array(z
196
+ .object({
197
+ id: segment,
198
+ path: relative,
199
+ kind: z.enum(["file", "directory"]),
200
+ required: z.boolean(),
201
+ backup: z.enum(["payload", "metadata"]).optional(),
202
+ })
203
+ .strict())
204
+ .max(16),
205
+ moves: z
206
+ .array(z
207
+ .object({
208
+ id: segment,
209
+ relative,
210
+ kind: z.enum(["file", "directory"]),
211
+ device: z.number().int().nonnegative(),
212
+ inode: z.number().int().nonnegative(),
213
+ })
214
+ .strict())
215
+ .max(16),
216
+ })
217
+ .strict()
218
+ .parse(plan);
219
+ z.enum(["adopt", "return"]).parse(direction);
220
+ const manifest = retainedStoreManifestSchema.parse({ protocol: 1, format: plan.format, resources: plan.slots });
221
+ if (plan.moves.some((move) => !manifest.resources.some((slot) => slot.id === move.id && slot.path === move.relative && slot.kind === move.kind)))
222
+ throw Error("Retained move differs from declared resources");
223
+ if (new Set(plan.moves.map((m) => m.id)).size !== plan.moves.length ||
224
+ plan.moves.some((m) => plan.moves.some((o) => o !== m && (m.relative === o.relative || o.relative.startsWith(m.relative + "/")))))
225
+ throw Error("Overlapping retained moves");
226
+ if (plan.directory === plan.source || plan.directory.startsWith(plan.source + path.sep) || plan.source.startsWith(plan.directory + path.sep))
227
+ throw Error("Nested retained move");
228
+ assertRetainedStorePath(plan.source);
229
+ assertRetainedStorePath(plan.directory);
230
+ if (!retainedStoreEntry(plan.directory)) {
231
+ fs.mkdirSync(plan.directory, { mode: 0o700 });
232
+ syncRetainedDirectory(path.dirname(plan.directory));
233
+ }
234
+ const directoryStat = fs.lstatSync(plan.directory);
235
+ if (!directoryStat.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)
236
+ throw Error("Unsafe retained store directory");
237
+ for (const move of plan.moves) {
238
+ const legacy = path.join(plan.source, move.relative), stable = path.join(plan.directory, move.id);
239
+ const [from, to] = direction === "adopt" ? [legacy, stable] : [stable, legacy];
240
+ if (!retainedStoreEntry(from) && matches(to, move))
241
+ continue;
242
+ if (!matches(from, move) || retainedStoreEntry(to))
243
+ throw Error("Retained resource changed or destination is occupied");
244
+ fs.renameSync(from, to);
245
+ syncRetainedDirectory(path.dirname(from));
246
+ syncRetainedDirectory(path.dirname(to));
247
+ if (!matches(to, move))
248
+ throw Error("Retained resource identity changed");
249
+ afterMove?.(move.id);
250
+ }
251
+ }
252
+ export function writeRetainedStoreBinding(instanceDirectory, binding) {
253
+ bindingSchema.parse(binding);
254
+ writeRetainedStoreJson(path.join(instanceDirectory, RETAINED_STORE_BINDING), binding);
255
+ }
256
+ export function loadRetainedStoreBinding(instanceDirectory, operationId) {
257
+ const file = path.join(instanceDirectory, RETAINED_STORE_BINDING);
258
+ if (!retainedStoreEntry(file))
259
+ return;
260
+ const s = fs.statSync(file);
261
+ if (s.mode & 0o077)
262
+ throw Error("Retained store binding must be private");
263
+ const binding = bindingSchema.parse(readRetainedStoreJson(file));
264
+ assertRetainedStorePath(binding.directory);
265
+ const runtimeRoot = process.env["UNS_RETAINED_STORE_ROOT"]?.trim();
266
+ if (runtimeRoot && path.dirname(binding.directory) !== runtimeRoot)
267
+ throw Error("Retained binding does not match the configured Runtime root");
268
+ const directoryStat = retainedStoreEntry(binding.directory);
269
+ if (!directoryStat?.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)
270
+ throw Error("Unsafe retained store directory");
271
+ available(path.dirname(binding.directory));
272
+ if (retainedStoreEntry(path.join(binding.directory, "store-operation.json")) &&
273
+ readRetainedStoreJson(path.join(binding.directory, "store-operation.json")).operationId !== operationId)
274
+ throw Error("Retained store operation is incomplete");
275
+ const meta = bindingSchema.parse(readRetainedStoreJson(path.join(binding.directory, "store.json")));
276
+ if (retainedStoreDigest(meta) !== retainedStoreDigest(binding))
277
+ throw Error("Retained store owner or generation changed");
278
+ return binding;
279
+ }
280
+ /** No application payload/schema inspection is performed by this helper. */
281
+ export function resolveRetainedStoreResources(instanceDirectory, manifest, expected) {
282
+ retainedStoreManifestSchema.parse(manifest);
283
+ if (retainedStoreEntry(path.join(instanceDirectory, RETAINED_STORE_GUARD)))
284
+ throw Error("Retained store operation has fenced this instance");
285
+ const binding = loadRetainedStoreBinding(instanceDirectory);
286
+ if (binding &&
287
+ expected &&
288
+ Object.entries(expected).some(([k, v]) => k === "version" ? binding.owner.version.replace(/^v/, "") !== String(v).replace(/^v/, "") : binding.owner[k] !== v))
289
+ throw Error("Retained binding does not name this runtime");
290
+ if (binding && binding.format !== manifest.format)
291
+ throw Error("Unsupported retained store format");
292
+ if (binding) {
293
+ if (manifest.storageProtocol !== 1)
294
+ throw Error("Bound retained stores require the shared storage protocol");
295
+ const declaration = retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(binding.directory, "store-resources.json")));
296
+ if (declaration.storageProtocol !== 1 ||
297
+ declaration.format !== manifest.format ||
298
+ retainedStoreDigest(declaration.resources) !== retainedStoreDigest(manifest.resources))
299
+ throw Error("Retained store resource declaration changed");
300
+ for (const resource of manifest.resources)
301
+ if (resource.required && !retainedStoreEntry(path.join(binding.directory, resource.id)))
302
+ throw Error("Required retained resource missing");
303
+ }
304
+ return {
305
+ binding,
306
+ resources: Object.fromEntries(manifest.resources.map((r) => [r.id, binding ? path.join(binding.directory, r.id) : path.join(instanceDirectory, r.path)])),
307
+ };
308
+ }
309
+ export const retainedStoreSelectionSchema = z
310
+ .object({
311
+ requestId: z.uuid(),
312
+ rttNode: segment,
313
+ sourceVersion: segment,
314
+ sourceInstanceId: segment,
315
+ targetVersion: segment,
316
+ targetInstanceId: segment,
317
+ })
318
+ .strict()
319
+ .refine((x) => x.sourceVersion !== x.targetVersion || x.sourceInstanceId !== x.targetInstanceId, "Source and target must differ");
320
+ export const retainedStoreRequestSchema = z.discriminatedUnion("action", [
321
+ z.object({ action: z.literal("review"), selection: retainedStoreSelectionSchema }).strict(),
322
+ z.object({ action: z.literal("get"), id: z.uuid() }).strict(),
323
+ z.object({ action: z.enum(["adopt", "return"]), id: z.uuid(), reviewedChecksum: z.string().regex(/^[a-f0-9]{64}$/) }).strict(),
324
+ ]);
325
+ /** Capture mutable control files from the current stopped owner before persisting return intent. */
326
+ export function prepareRetainedStoreReturn(plan) {
327
+ const moves = [];
328
+ for (const slot of plan.slots) {
329
+ const stable = path.join(plan.directory, slot.id), legacy = path.join(plan.source, slot.path);
330
+ assertRetainedStorePath(stable);
331
+ assertRetainedStorePath(legacy);
332
+ const a = retainedStoreEntry(stable), b = retainedStoreEntry(legacy);
333
+ if (a && b)
334
+ throw Error("Return destination is occupied");
335
+ if (a) {
336
+ if (a.uid !== process.getuid?.() || a.isSymbolicLink() || (slot.kind === "file" ? !a.isFile() || a.nlink !== 1 : !a.isDirectory()))
337
+ throw Error("Unsafe current retained resource");
338
+ moves.push({ id: slot.id, relative: slot.path, kind: slot.kind, device: a.dev, inode: a.ino });
339
+ }
340
+ else if (b) {
341
+ const original = plan.moves.find((m) => m.id === slot.id);
342
+ if (!original || !matches(legacy, original))
343
+ throw Error("Return source identity changed");
344
+ moves.push(original);
345
+ }
346
+ else if (slot.required)
347
+ throw Error("Current retained resource missing");
348
+ }
349
+ return { ...plan, moves };
350
+ }
351
+ export function retainedStoreRoot(defaultBase, configured) {
352
+ const root = configured?.trim() || path.join(defaultBase, "retained-stores");
353
+ assertRetainedStorePath(root);
354
+ return root;
355
+ }
356
+ export function openRetainedStoreLease(binding) {
357
+ return runtimeLease(binding);
358
+ }
359
+ export function openRetainedStoreMaintenance(directory) {
360
+ return maintenanceLease(directory);
361
+ }
362
+ export const retainedStoreBackupSchema = z
363
+ .object({
364
+ schemaVersion: z.literal(1),
365
+ state: z.enum(["included", "excluded", "empty"]),
366
+ rootSha256: z.string().regex(/^[a-f0-9]{64}$/),
367
+ stores: z.number().int().min(0).max(32),
368
+ files: z.number().int().min(0).max(100000),
369
+ bytes: z.number().int().positive().max(68753031168),
370
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
371
+ })
372
+ .strict();
373
+ //# sourceMappingURL=retained-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retained-store.js","sourceRoot":"","sources":["../../src/runtime/retained-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\nimport { available, maintenanceLease, runtimeLease } from \"./retained-store-storage.mjs\";\n\nconst segment = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/);\nconst relative = z\n .string()\n .min(1)\n .max(256)\n .refine((value) => !path.isAbsolute(value) && !value.includes(\"\\\\\") && value.split(\"/\").every((p) => /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(p)));\nexport const RETAINED_STORE_MANIFEST = \"retained-store.manifest.json\";\nexport const RETAINED_STORE_BINDING = \"retained-store-binding.json\";\nexport const RETAINED_STORE_GUARD = \"retained-store-guard.json\";\nexport const retainedStoreManifestSchema = z\n .object({\n protocol: z.literal(1),\n storageProtocol: z.literal(1).optional(),\n format: segment,\n resources: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .min(1)\n .max(16),\n legacyVersions: z.array(segment).max(32).default([]),\n })\n .strict()\n .superRefine((m, ctx) => {\n if (new Set(m.resources.map((r) => r.id)).size !== m.resources.length) ctx.addIssue({ code: \"custom\", message: \"Duplicate resource ID\" });\n for (const r of m.resources) {\n if ([\"store.json\", \"store-resources.json\", \"store-operation.json\", \"store-lease.sqlite\", \"store-returned.json\"].includes(r.id))\n ctx.addIssue({ code: \"custom\", message: \"Reserved store metadata resource\" });\n if ([\"config.json\", \"controller-state.json\", RETAINED_STORE_BINDING, RETAINED_STORE_GUARD].includes(r.path))\n ctx.addIssue({ code: \"custom\", message: \"Reserved instance resource\" });\n if (m.resources.some((o) => o !== r && (o.path === r.path || o.path.startsWith(r.path + \"/\"))))\n ctx.addIssue({ code: \"custom\", message: \"Overlapping resources\" });\n }\n });\nexport type RetainedStoreManifest = z.infer<typeof retainedStoreManifestSchema>;\nexport type StoreOwner = { rttNode: string; version: string; instanceId: string };\nexport type StoreBinding = {\n protocol: 1;\n storeId: string;\n generation: string;\n operationId: string;\n directory: string;\n format: string;\n configurationDigest: string;\n owner: StoreOwner;\n maintenance: true;\n};\nexport type ResourceMove = { id: string; relative: string; kind: \"file\" | \"directory\"; device: number; inode: number };\nexport type StoreMovePlan = {\n storeId: string;\n generation: string;\n format: string;\n source: string;\n directory: string;\n slots: RetainedStoreManifest[\"resources\"];\n moves: ResourceMove[];\n};\nconst ownerSchema = z.object({ rttNode: segment, version: segment, instanceId: segment }).strict();\nconst bindingSchema = z\n .object({\n protocol: z.literal(1),\n storeId: z.uuid(),\n generation: z.uuid(),\n operationId: z.uuid(),\n directory: z.string().max(4096),\n format: segment,\n configurationDigest: z.string().regex(/^[a-f0-9]{64}$/),\n owner: ownerSchema,\n maintenance: z.literal(true),\n })\n .strict();\nexport function retainedStoreDigest(value: unknown): string {\n const stable = (v: any): any =>\n Array.isArray(v)\n ? v.map(stable)\n : v && typeof v === \"object\"\n ? Object.fromEntries(\n Object.keys(v)\n .sort()\n .map((k) => [k, stable(v[k])]),\n )\n : v;\n return createHash(\"sha256\")\n .update(JSON.stringify(stable(value)))\n .digest(\"hex\");\n}\nexport function retainedStoreEntry(file: string): fs.Stats | undefined {\n try {\n return fs.lstatSync(file);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return;\n throw e;\n }\n}\nexport function assertRetainedStorePath(file: string): void {\n if (!path.isAbsolute(file) || path.normalize(file) !== file) throw Error(\"Unnormalized retained store path\");\n for (let p = file; p !== path.dirname(p); p = path.dirname(p)) {\n const s = retainedStoreEntry(p);\n if (s?.isSymbolicLink()) throw Error(\"Linked retained store path\");\n }\n}\nexport function readRetainedStoreJson(file: string): unknown {\n assertRetainedStorePath(file);\n const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);\n try {\n const s = fs.fstatSync(fd);\n if (!s.isFile() || s.nlink !== 1 || s.size > 128 * 1024 || s.uid !== process.getuid?.()) throw Error(\"Unsafe retained store metadata\");\n return JSON.parse(fs.readFileSync(fd, \"utf8\"));\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function syncRetainedDirectory(directory: string): void {\n const fd = fs.openSync(directory, \"r\");\n try {\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n}\nexport function writeRetainedStoreJson(file: string, value: unknown): void {\n assertRetainedStorePath(file);\n const temporary = file + \".\" + randomUUID() + \".tmp\";\n const text = JSON.stringify(value);\n if (Buffer.byteLength(text) > 128 * 1024) throw Error(\"Retained store metadata too large\");\n try {\n const fd = fs.openSync(temporary, \"wx\", 0o600);\n try {\n fs.writeFileSync(fd, text);\n fs.fsyncSync(fd);\n } finally {\n fs.closeSync(fd);\n }\n fs.renameSync(temporary, file);\n syncRetainedDirectory(path.dirname(file));\n } finally {\n if (retainedStoreEntry(temporary)) fs.unlinkSync(temporary);\n }\n}\nexport function readRetainedStoreManifest(packageDirectory: string): RetainedStoreManifest {\n return retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(packageDirectory, RETAINED_STORE_MANIFEST)));\n}\nexport function prepareRetainedStoreMove(source: string, root: string, storeId: string, manifest: RetainedStoreManifest): StoreMovePlan {\n z.uuid().parse(storeId);\n retainedStoreManifestSchema.parse(manifest);\n assertRetainedStorePath(source);\n assertRetainedStorePath(root);\n const directory = path.join(root, storeId);\n if (retainedStoreEntry(directory)) throw Error(\"Retained store destination exists\");\n const existingRoot = retainedStoreEntry(root);\n if (existingRoot && (!existingRoot.isDirectory() || existingRoot.uid !== process.getuid?.() || existingRoot.mode & 0o077))\n throw Error(\"Retained root must be private and owned\");\n const rootStat = existingRoot ?? fs.statSync(path.dirname(root)),\n sourceStat = fs.statSync(source);\n if (root === source || root.startsWith(source + path.sep) || source.startsWith(root + path.sep)) throw Error(\"Nested retained store roots\");\n if (rootStat.dev !== sourceStat.dev) throw Error(\"Retained store requires same filesystem; copy is not permitted\");\n const moves: ResourceMove[] = [];\n for (const resource of manifest.resources) {\n const file = path.join(source, resource.path);\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n if (!s) {\n if (resource.required) throw Error(\"Required retained resource missing\");\n continue;\n }\n if (\n s.uid !== process.getuid?.() ||\n s.dev !== rootStat.dev ||\n s.isSymbolicLink() ||\n (resource.kind === \"file\" ? !s.isFile() || s.nlink !== 1 : !s.isDirectory())\n )\n throw Error(\"Unsafe retained resource\");\n moves.push({ id: resource.id, relative: resource.path, kind: resource.kind, device: s.dev, inode: s.ino });\n }\n return { storeId, generation: randomUUID(), format: manifest.format, source, directory, slots: manifest.resources, moves };\n}\nfunction matches(file: string, move: ResourceMove): boolean {\n assertRetainedStorePath(file);\n const s = retainedStoreEntry(file);\n return (\n !!s &&\n !s.isSymbolicLink() &&\n s.dev === move.device &&\n s.ino === move.inode &&\n s.uid === process.getuid?.() &&\n (move.kind === \"file\" ? s.isFile() && s.nlink === 1 : s.isDirectory())\n );\n}\n/** Caller durably records the complete plan and holds its resource reservation first.\n * Each rename is atomic; the multi-resource operation is deliberately resumable. */\nexport function applyRetainedStoreMove(plan: StoreMovePlan, direction: \"adopt\" | \"return\", afterMove?: (id: string) => void): void {\n z.object({\n storeId: z.uuid(),\n generation: z.uuid(),\n format: segment,\n source: z.string().max(4096),\n directory: z.string().max(4096),\n slots: z\n .array(\n z\n .object({\n id: segment,\n path: relative,\n kind: z.enum([\"file\", \"directory\"]),\n required: z.boolean(),\n backup: z.enum([\"payload\", \"metadata\"]).optional(),\n })\n .strict(),\n )\n .max(16),\n moves: z\n .array(\n z\n .object({\n id: segment,\n relative,\n kind: z.enum([\"file\", \"directory\"]),\n device: z.number().int().nonnegative(),\n inode: z.number().int().nonnegative(),\n })\n .strict(),\n )\n .max(16),\n })\n .strict()\n .parse(plan);\n z.enum([\"adopt\", \"return\"]).parse(direction);\n const manifest = retainedStoreManifestSchema.parse({ protocol: 1, format: plan.format, resources: plan.slots });\n if (plan.moves.some((move) => !manifest.resources.some((slot) => slot.id === move.id && slot.path === move.relative && slot.kind === move.kind)))\n throw Error(\"Retained move differs from declared resources\");\n if (\n new Set(plan.moves.map((m) => m.id)).size !== plan.moves.length ||\n plan.moves.some((m) => plan.moves.some((o) => o !== m && (m.relative === o.relative || o.relative.startsWith(m.relative + \"/\"))))\n )\n throw Error(\"Overlapping retained moves\");\n if (plan.directory === plan.source || plan.directory.startsWith(plan.source + path.sep) || plan.source.startsWith(plan.directory + path.sep))\n throw Error(\"Nested retained move\");\n assertRetainedStorePath(plan.source);\n assertRetainedStorePath(plan.directory);\n if (!retainedStoreEntry(plan.directory)) {\n fs.mkdirSync(plan.directory, { mode: 0o700 });\n syncRetainedDirectory(path.dirname(plan.directory));\n }\n const directoryStat = fs.lstatSync(plan.directory);\n if (!directoryStat.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n for (const move of plan.moves) {\n const legacy = path.join(plan.source, move.relative),\n stable = path.join(plan.directory, move.id);\n const [from, to] = direction === \"adopt\" ? [legacy, stable] : [stable, legacy];\n if (!retainedStoreEntry(from) && matches(to, move)) continue;\n if (!matches(from, move) || retainedStoreEntry(to)) throw Error(\"Retained resource changed or destination is occupied\");\n fs.renameSync(from, to);\n syncRetainedDirectory(path.dirname(from));\n syncRetainedDirectory(path.dirname(to));\n if (!matches(to, move)) throw Error(\"Retained resource identity changed\");\n afterMove?.(move.id);\n }\n}\nexport function writeRetainedStoreBinding(instanceDirectory: string, binding: StoreBinding): void {\n bindingSchema.parse(binding);\n writeRetainedStoreJson(path.join(instanceDirectory, RETAINED_STORE_BINDING), binding);\n}\nexport function loadRetainedStoreBinding(instanceDirectory: string, operationId?: string): StoreBinding | undefined {\n const file = path.join(instanceDirectory, RETAINED_STORE_BINDING);\n if (!retainedStoreEntry(file)) return;\n const s = fs.statSync(file);\n if (s.mode & 0o077) throw Error(\"Retained store binding must be private\");\n const binding = bindingSchema.parse(readRetainedStoreJson(file));\n assertRetainedStorePath(binding.directory);\n const runtimeRoot = process.env[\"UNS_RETAINED_STORE_ROOT\"]?.trim();\n if (runtimeRoot && path.dirname(binding.directory) !== runtimeRoot) throw Error(\"Retained binding does not match the configured Runtime root\");\n const directoryStat = retainedStoreEntry(binding.directory);\n if (!directoryStat?.isDirectory() || directoryStat.uid !== process.getuid?.() || directoryStat.mode & 0o077)\n throw Error(\"Unsafe retained store directory\");\n available(path.dirname(binding.directory));\n if (\n retainedStoreEntry(path.join(binding.directory, \"store-operation.json\")) &&\n (readRetainedStoreJson(path.join(binding.directory, \"store-operation.json\")) as any).operationId !== operationId\n )\n throw Error(\"Retained store operation is incomplete\");\n const meta = bindingSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store.json\")));\n if (retainedStoreDigest(meta) !== retainedStoreDigest(binding)) throw Error(\"Retained store owner or generation changed\");\n return binding;\n}\n/** No application payload/schema inspection is performed by this helper. */\nexport function resolveRetainedStoreResources(\n instanceDirectory: string,\n manifest: RetainedStoreManifest,\n expected?: Partial<StoreOwner>,\n): { binding?: StoreBinding; resources: Record<string, string> } {\n retainedStoreManifestSchema.parse(manifest);\n if (retainedStoreEntry(path.join(instanceDirectory, RETAINED_STORE_GUARD))) throw Error(\"Retained store operation has fenced this instance\");\n const binding = loadRetainedStoreBinding(instanceDirectory);\n if (\n binding &&\n expected &&\n Object.entries(expected).some(([k, v]) =>\n k === \"version\" ? binding.owner.version.replace(/^v/, \"\") !== String(v).replace(/^v/, \"\") : (binding.owner as any)[k] !== v,\n )\n )\n throw Error(\"Retained binding does not name this runtime\");\n if (binding && binding.format !== manifest.format) throw Error(\"Unsupported retained store format\");\n if (binding) {\n if (manifest.storageProtocol !== 1) throw Error(\"Bound retained stores require the shared storage protocol\");\n const declaration = retainedStoreManifestSchema.parse(readRetainedStoreJson(path.join(binding.directory, \"store-resources.json\")));\n if (\n declaration.storageProtocol !== 1 ||\n declaration.format !== manifest.format ||\n retainedStoreDigest(declaration.resources) !== retainedStoreDigest(manifest.resources)\n )\n throw Error(\"Retained store resource declaration changed\");\n for (const resource of manifest.resources)\n if (resource.required && !retainedStoreEntry(path.join(binding.directory, resource.id))) throw Error(\"Required retained resource missing\");\n }\n return {\n binding,\n resources: Object.fromEntries(\n manifest.resources.map((r) => [r.id, binding ? path.join(binding.directory, r.id) : path.join(instanceDirectory, r.path)]),\n ),\n };\n}\n\nexport const retainedStoreSelectionSchema = z\n .object({\n requestId: z.uuid(),\n rttNode: segment,\n sourceVersion: segment,\n sourceInstanceId: segment,\n targetVersion: segment,\n targetInstanceId: segment,\n })\n .strict()\n .refine((x) => x.sourceVersion !== x.targetVersion || x.sourceInstanceId !== x.targetInstanceId, \"Source and target must differ\");\nexport const retainedStoreRequestSchema = z.discriminatedUnion(\"action\", [\n z.object({ action: z.literal(\"review\"), selection: retainedStoreSelectionSchema }).strict(),\n z.object({ action: z.literal(\"get\"), id: z.uuid() }).strict(),\n z.object({ action: z.enum([\"adopt\", \"return\"]), id: z.uuid(), reviewedChecksum: z.string().regex(/^[a-f0-9]{64}$/) }).strict(),\n]);\nexport type RetainedStoreRequest = z.infer<typeof retainedStoreRequestSchema>;\n\n/** Capture mutable control files from the current stopped owner before persisting return intent. */\nexport function prepareRetainedStoreReturn(plan: StoreMovePlan): StoreMovePlan {\n const moves: ResourceMove[] = [];\n for (const slot of plan.slots) {\n const stable = path.join(plan.directory, slot.id),\n legacy = path.join(plan.source, slot.path);\n assertRetainedStorePath(stable);\n assertRetainedStorePath(legacy);\n const a = retainedStoreEntry(stable),\n b = retainedStoreEntry(legacy);\n if (a && b) throw Error(\"Return destination is occupied\");\n if (a) {\n if (a.uid !== process.getuid?.() || a.isSymbolicLink() || (slot.kind === \"file\" ? !a.isFile() || a.nlink !== 1 : !a.isDirectory()))\n throw Error(\"Unsafe current retained resource\");\n moves.push({ id: slot.id, relative: slot.path, kind: slot.kind, device: a.dev, inode: a.ino });\n } else if (b) {\n const original = plan.moves.find((m) => m.id === slot.id);\n if (!original || !matches(legacy, original)) throw Error(\"Return source identity changed\");\n moves.push(original);\n } else if (slot.required) throw Error(\"Current retained resource missing\");\n }\n return { ...plan, moves };\n}\n\nexport function retainedStoreRoot(defaultBase: string, configured?: string): string {\n const root = configured?.trim() || path.join(defaultBase, \"retained-stores\");\n assertRetainedStorePath(root);\n return root;\n}\nexport function openRetainedStoreLease(binding: StoreBinding): { close: () => void } {\n return runtimeLease(binding);\n}\nexport function openRetainedStoreMaintenance(directory: string): () => void {\n return maintenanceLease(directory);\n}\nexport const retainedStoreBackupSchema = z\n .object({\n schemaVersion: z.literal(1),\n state: z.enum([\"included\", \"excluded\", \"empty\"]),\n rootSha256: z.string().regex(/^[a-f0-9]{64}$/),\n stores: z.number().int().min(0).max(32),\n files: z.number().int().min(0).max(100000),\n bytes: z.number().int().positive().max(68753031168),\n sha256: z.string().regex(/^[a-f0-9]{64}$/),\n })\n .strict();\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uns-kit/core",
3
- "version": "3.0.44",
3
+ "version": "3.0.45",
4
4
  "description": "Core utilities and runtime building blocks for UNS-based realtime transformers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,15 @@
23
23
  "MIGRATIONS.md"
24
24
  ],
25
25
  "exports": {
26
+ "./runtime/retained-store.js": {
27
+ "types": "./dist/runtime/retained-store.d.ts",
28
+ "import": "./dist/runtime/retained-store.js"
29
+ },
30
+ "./runtime/retained-store-storage.mjs": {
31
+ "types": "./dist/runtime/retained-store-storage.d.mts",
32
+ "import": "./dist/runtime/retained-store-storage.mjs"
33
+ },
34
+ "./runtime/retained-store-storage-contract.json": "./dist/runtime/retained-store-storage-contract.json",
26
35
  "./*": "./dist/*",
27
36
  ".": {
28
37
  "types": "./dist/index.d.ts",