@volter/world-core 3.0.148 → 3.0.149
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/dist/src/resource-blob.d.ts +7 -0
- package/dist/src/resource-blob.js +17 -5
- package/dist/src/runtime.d.ts +1 -1
- package/dist/src/runtime.js +1 -1
- package/package.json +1 -1
- package/src/resource-blob.ts +19 -5
- package/src/runtime.ts +1 -1
|
@@ -8,6 +8,13 @@ export declare function removeResourceBlob(service: string, key: string, root?:
|
|
|
8
8
|
/** A service's resources dir at this branch, then at each retained ancestor's: where a read resolves a payload, and
|
|
9
9
|
* what a listing that mints ids covers, so a branch serves bytes its parent received and never re-mints a parent's id. */
|
|
10
10
|
export declare function resourceChain(service: string, root?: string): string[];
|
|
11
|
+
/** Where a resource blob's key is kept under a resources directory. A key is a relative path, its parts joined by '/'.
|
|
12
|
+
* On Windows a part may hold a character no file name can: a ':' there names an NTFS stream of the file before it, so
|
|
13
|
+
* `asset:<id>` wrote into a stream of a file named `asset` and the store's rename failed (EINVAL). There each of
|
|
14
|
+
* `<>:"|?*`, a control character and '%' itself is written as %XX, one name per key; elsewhere the key is the path. */
|
|
15
|
+
export declare function resourceBlobFile(dir: string, key: string): string;
|
|
16
|
+
/** The key a file under a resources directory holds: resourceBlobFile's inverse, its parts joined by '/'. */
|
|
17
|
+
export declare function resourceBlobKey(dir: string, path: string): string;
|
|
11
18
|
/** Size of a resource payload at this branch or its ancestors, or null. */
|
|
12
19
|
export declare function resourceBlobSize(service: string, key: string, root?: string): Promise<number | null>;
|
|
13
20
|
/** A byte range of a resource payload at this branch or its ancestors (a ranged read where the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAbsolute, join } from 'node:path';
|
|
1
|
+
import { isAbsolute, join, relative } from 'node:path';
|
|
2
2
|
import { getActiveBlobStore, readBlobRange } from "./blob-store.js";
|
|
3
3
|
import { readBranchMeta } from "./log.js";
|
|
4
4
|
import { worldPaths } from "./storage.js";
|
|
@@ -6,7 +6,7 @@ import { worldPaths } from "./storage.js";
|
|
|
6
6
|
export async function readResourceBlob(service, key, root) {
|
|
7
7
|
assertResourceKey(key);
|
|
8
8
|
for (const dir of resourceChain(service, root)) {
|
|
9
|
-
const bytes = await getActiveBlobStore().get(
|
|
9
|
+
const bytes = await getActiveBlobStore().get(resourceBlobFile(dir, key));
|
|
10
10
|
if (bytes !== null)
|
|
11
11
|
return bytes;
|
|
12
12
|
}
|
|
@@ -15,13 +15,13 @@ export async function readResourceBlob(service, key, root) {
|
|
|
15
15
|
/** Write a resource payload on this branch (a stored object's bytes), where a read finds it first. */
|
|
16
16
|
export async function writeResourceBlob(service, key, bytes, root) {
|
|
17
17
|
assertResourceKey(key);
|
|
18
|
-
await getActiveBlobStore().put(
|
|
18
|
+
await getActiveBlobStore().put(resourceBlobFile(worldPaths(service, root).resources, key), bytes);
|
|
19
19
|
}
|
|
20
20
|
/** Remove this branch's copy of a resource payload; an ancestor's stays its own (the row that named it is what a
|
|
21
21
|
* deletion ends). */
|
|
22
22
|
export async function removeResourceBlob(service, key, root) {
|
|
23
23
|
assertResourceKey(key);
|
|
24
|
-
await getActiveBlobStore().remove(
|
|
24
|
+
await getActiveBlobStore().remove(resourceBlobFile(worldPaths(service, root).resources, key));
|
|
25
25
|
}
|
|
26
26
|
/** A service's resources dir at this branch, then at each retained ancestor's: where a read resolves a payload, and
|
|
27
27
|
* what a listing that mints ids covers, so a branch serves bytes its parent received and never re-mints a parent's id. */
|
|
@@ -40,6 +40,18 @@ export function resourceChain(service, root) {
|
|
|
40
40
|
current = parent.at;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
/** Where a resource blob's key is kept under a resources directory. A key is a relative path, its parts joined by '/'.
|
|
44
|
+
* On Windows a part may hold a character no file name can: a ':' there names an NTFS stream of the file before it, so
|
|
45
|
+
* `asset:<id>` wrote into a stream of a file named `asset` and the store's rename failed (EINVAL). There each of
|
|
46
|
+
* `<>:"|?*`, a control character and '%' itself is written as %XX, one name per key; elsewhere the key is the path. */
|
|
47
|
+
export function resourceBlobFile(dir, key) {
|
|
48
|
+
return join(dir, process.platform === 'win32' ? key.replace(/[%<>:"|?*\x00-\x1f]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`) : key);
|
|
49
|
+
}
|
|
50
|
+
/** The key a file under a resources directory holds: resourceBlobFile's inverse, its parts joined by '/'. */
|
|
51
|
+
export function resourceBlobKey(dir, path) {
|
|
52
|
+
const key = relative(dir, path).split('\\').join('/');
|
|
53
|
+
return process.platform === 'win32' ? key.replace(/%([0-9A-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))) : key;
|
|
54
|
+
}
|
|
43
55
|
function assertResourceKey(key) {
|
|
44
56
|
if (isAbsolute(key) || key.includes('\0') || key.split(/[\\/]/).includes('..'))
|
|
45
57
|
throw new Error('Invalid relative resource blob key');
|
|
@@ -48,7 +60,7 @@ function assertResourceKey(key) {
|
|
|
48
60
|
async function resolveResourceBlob(service, key, root) {
|
|
49
61
|
assertResourceKey(key);
|
|
50
62
|
for (const dir of resourceChain(service, root)) {
|
|
51
|
-
const path =
|
|
63
|
+
const path = resourceBlobFile(dir, key);
|
|
52
64
|
if (await getActiveBlobStore().exists(path))
|
|
53
65
|
return path;
|
|
54
66
|
}
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ export { corsHeaders, withCors, type CorsDecl } from './cors.js';
|
|
|
78
78
|
export { setInProcessTwins, vendorFetch, VendorUnreachableError, type InProcessTwin } from './vendor-call.js';
|
|
79
79
|
export { closeManagedDatabases, databaseSession, EngineDatabaseError, EngineUnavailableError, managedDatabase, quoteIdent, useDatabaseDriver, type DatabaseDriver, type DatabaseSession } from './managed-database.js';
|
|
80
80
|
export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.js';
|
|
81
|
-
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobSize, resourceChain, writeResourceBlob } from './resource-blob.js';
|
|
81
|
+
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobFile, resourceBlobKey, resourceBlobSize, resourceChain, writeResourceBlob } from './resource-blob.js';
|
|
82
82
|
export { assertStateRemovable, checkParent, stateGeneration, withAncestryLock, withHistoryLock, withHistoryLocks, withStateLock, withStateRemoval } from './ancestry.js';
|
|
83
83
|
export { captureHistory, captureParentHistory, historyChanges, historyAtInstant, historyDigest, historyEntries, historyLength, historyPrefix, inheritedHistory, originHead, publishOriginHistory, readHistoryView } from './history.js';
|
|
84
84
|
export type { HistoryView, HistoryLayout, HistoryReference, HistoryOrigin } from './history.js';
|
package/dist/src/runtime.js
CHANGED
|
@@ -98,7 +98,7 @@ export { multipartBoundary, multipartParts, multipartEnvelope, inferMultipartBou
|
|
|
98
98
|
export { corsHeaders, withCors } from "./cors.js";
|
|
99
99
|
export { setInProcessTwins, vendorFetch, VendorUnreachableError } from "./vendor-call.js";
|
|
100
100
|
export { closeManagedDatabases, databaseSession, EngineDatabaseError, EngineUnavailableError, managedDatabase, quoteIdent, useDatabaseDriver } from "./managed-database.js";
|
|
101
|
-
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobSize, resourceChain, writeResourceBlob } from "./resource-blob.js";
|
|
101
|
+
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobFile, resourceBlobKey, resourceBlobSize, resourceChain, writeResourceBlob } from "./resource-blob.js";
|
|
102
102
|
export { assertStateRemovable, checkParent, stateGeneration, withAncestryLock, withHistoryLock, withHistoryLocks, withStateLock, withStateRemoval } from "./ancestry.js";
|
|
103
103
|
export { captureHistory, captureParentHistory, historyChanges, historyAtInstant, historyDigest, historyEntries, historyLength, historyPrefix, inheritedHistory, originHead, publishOriginHistory, readHistoryView } from "./history.js";
|
|
104
104
|
export { foldHistory } from "./log.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.149",
|
|
4
4
|
"description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
package/src/resource-blob.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAbsolute, join } from 'node:path';
|
|
1
|
+
import { isAbsolute, join, relative } from 'node:path';
|
|
2
2
|
import { getActiveBlobStore, readBlobRange } from './blob-store.ts';
|
|
3
3
|
import { readBranchMeta } from './log.ts';
|
|
4
4
|
import { worldPaths } from './storage.ts';
|
|
@@ -7,7 +7,7 @@ import { worldPaths } from './storage.ts';
|
|
|
7
7
|
export async function readResourceBlob(service: string, key: string, root?: string): Promise<Uint8Array | null> {
|
|
8
8
|
assertResourceKey(key);
|
|
9
9
|
for (const dir of resourceChain(service, root)) {
|
|
10
|
-
const bytes = await getActiveBlobStore().get(
|
|
10
|
+
const bytes = await getActiveBlobStore().get(resourceBlobFile(dir, key));
|
|
11
11
|
if (bytes !== null) return bytes;
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
@@ -16,14 +16,14 @@ export async function readResourceBlob(service: string, key: string, root?: stri
|
|
|
16
16
|
/** Write a resource payload on this branch (a stored object's bytes), where a read finds it first. */
|
|
17
17
|
export async function writeResourceBlob(service: string, key: string, bytes: Uint8Array, root?: string): Promise<void> {
|
|
18
18
|
assertResourceKey(key);
|
|
19
|
-
await getActiveBlobStore().put(
|
|
19
|
+
await getActiveBlobStore().put(resourceBlobFile(worldPaths(service, root).resources, key), bytes);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Remove this branch's copy of a resource payload; an ancestor's stays its own (the row that named it is what a
|
|
23
23
|
* deletion ends). */
|
|
24
24
|
export async function removeResourceBlob(service: string, key: string, root?: string): Promise<void> {
|
|
25
25
|
assertResourceKey(key);
|
|
26
|
-
await getActiveBlobStore().remove(
|
|
26
|
+
await getActiveBlobStore().remove(resourceBlobFile(worldPaths(service, root).resources, key));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** A service's resources dir at this branch, then at each retained ancestor's: where a read resolves a payload, and
|
|
@@ -42,6 +42,20 @@ export function resourceChain(service: string, root?: string): string[] {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/** Where a resource blob's key is kept under a resources directory. A key is a relative path, its parts joined by '/'.
|
|
46
|
+
* On Windows a part may hold a character no file name can: a ':' there names an NTFS stream of the file before it, so
|
|
47
|
+
* `asset:<id>` wrote into a stream of a file named `asset` and the store's rename failed (EINVAL). There each of
|
|
48
|
+
* `<>:"|?*`, a control character and '%' itself is written as %XX, one name per key; elsewhere the key is the path. */
|
|
49
|
+
export function resourceBlobFile(dir: string, key: string): string {
|
|
50
|
+
return join(dir, process.platform === 'win32' ? key.replace(/[%<>:"|?*\x00-\x1f]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`) : key);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The key a file under a resources directory holds: resourceBlobFile's inverse, its parts joined by '/'. */
|
|
54
|
+
export function resourceBlobKey(dir: string, path: string): string {
|
|
55
|
+
const key = relative(dir, path).split('\\').join('/');
|
|
56
|
+
return process.platform === 'win32' ? key.replace(/%([0-9A-F]{2})/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16))) : key;
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
function assertResourceKey(key: string): void {
|
|
46
60
|
if (isAbsolute(key) || key.includes('\0') || key.split(/[\\/]/).includes('..')) throw new Error('Invalid relative resource blob key');
|
|
47
61
|
}
|
|
@@ -50,7 +64,7 @@ function assertResourceKey(key: string): void {
|
|
|
50
64
|
async function resolveResourceBlob(service: string, key: string, root?: string): Promise<string | null> {
|
|
51
65
|
assertResourceKey(key);
|
|
52
66
|
for (const dir of resourceChain(service, root)) {
|
|
53
|
-
const path =
|
|
67
|
+
const path = resourceBlobFile(dir, key);
|
|
54
68
|
if (await getActiveBlobStore().exists(path)) return path;
|
|
55
69
|
}
|
|
56
70
|
return null;
|
package/src/runtime.ts
CHANGED
|
@@ -335,7 +335,7 @@ export { corsHeaders, withCors, type CorsDecl } from './cors.ts';
|
|
|
335
335
|
export { setInProcessTwins, vendorFetch, VendorUnreachableError, type InProcessTwin } from './vendor-call.ts';
|
|
336
336
|
export { closeManagedDatabases, databaseSession, EngineDatabaseError, EngineUnavailableError, managedDatabase, quoteIdent, useDatabaseDriver, type DatabaseDriver, type DatabaseSession } from './managed-database.ts';
|
|
337
337
|
export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.ts';
|
|
338
|
-
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobSize, resourceChain, writeResourceBlob } from './resource-blob.ts';
|
|
338
|
+
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobFile, resourceBlobKey, resourceBlobSize, resourceChain, writeResourceBlob } from './resource-blob.ts';
|
|
339
339
|
|
|
340
340
|
export { assertStateRemovable, checkParent, stateGeneration, withAncestryLock, withHistoryLock, withHistoryLocks, withStateLock, withStateRemoval } from './ancestry.ts';
|
|
341
341
|
|