@neottia/repository-store 0.2.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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/binding.gyp +9 -0
  4. package/dist/errors.d.ts +43 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/errors.js +69 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/files.d.ts +19 -0
  9. package/dist/files.d.ts.map +1 -0
  10. package/dist/files.js +113 -0
  11. package/dist/files.js.map +1 -0
  12. package/dist/index.d.ts +14 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +11 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/internal/fault-injection.d.ts +13 -0
  17. package/dist/internal/fault-injection.d.ts.map +1 -0
  18. package/dist/internal/fault-injection.js +68 -0
  19. package/dist/internal/fault-injection.js.map +1 -0
  20. package/dist/internal/filesystem.d.ts +25 -0
  21. package/dist/internal/filesystem.d.ts.map +1 -0
  22. package/dist/internal/filesystem.js +199 -0
  23. package/dist/internal/filesystem.js.map +1 -0
  24. package/dist/internal/model.d.ts +43 -0
  25. package/dist/internal/model.d.ts.map +1 -0
  26. package/dist/internal/model.js +49 -0
  27. package/dist/internal/model.js.map +1 -0
  28. package/dist/internal/native-publication.d.ts +9 -0
  29. package/dist/internal/native-publication.d.ts.map +1 -0
  30. package/dist/internal/native-publication.js +65 -0
  31. package/dist/internal/native-publication.js.map +1 -0
  32. package/dist/internal/publication.d.ts +10 -0
  33. package/dist/internal/publication.d.ts.map +1 -0
  34. package/dist/internal/publication.js +223 -0
  35. package/dist/internal/publication.js.map +1 -0
  36. package/dist/lease.d.ts +24 -0
  37. package/dist/lease.d.ts.map +1 -0
  38. package/dist/lease.js +932 -0
  39. package/dist/lease.js.map +1 -0
  40. package/dist/limits.d.ts +15 -0
  41. package/dist/limits.d.ts.map +1 -0
  42. package/dist/limits.js +14 -0
  43. package/dist/limits.js.map +1 -0
  44. package/dist/paths.d.ts +15 -0
  45. package/dist/paths.d.ts.map +1 -0
  46. package/dist/paths.js +58 -0
  47. package/dist/paths.js.map +1 -0
  48. package/dist/revision.d.ts +4 -0
  49. package/dist/revision.d.ts.map +1 -0
  50. package/dist/revision.js +8 -0
  51. package/dist/revision.js.map +1 -0
  52. package/dist/sqlite/adapter.d.ts +48 -0
  53. package/dist/sqlite/adapter.d.ts.map +1 -0
  54. package/dist/sqlite/adapter.js +183 -0
  55. package/dist/sqlite/adapter.js.map +1 -0
  56. package/dist/sqlite/bun.d.ts +2 -0
  57. package/dist/sqlite/bun.d.ts.map +1 -0
  58. package/dist/sqlite/bun.js +7 -0
  59. package/dist/sqlite/bun.js.map +1 -0
  60. package/dist/sqlite/cache-publication.d.ts +6 -0
  61. package/dist/sqlite/cache-publication.d.ts.map +1 -0
  62. package/dist/sqlite/cache-publication.js +191 -0
  63. package/dist/sqlite/cache-publication.js.map +1 -0
  64. package/dist/sqlite/cache.d.ts +26 -0
  65. package/dist/sqlite/cache.d.ts.map +1 -0
  66. package/dist/sqlite/cache.js +527 -0
  67. package/dist/sqlite/cache.js.map +1 -0
  68. package/dist/sqlite/node.d.ts +2 -0
  69. package/dist/sqlite/node.d.ts.map +1 -0
  70. package/dist/sqlite/node.js +6 -0
  71. package/dist/sqlite/node.js.map +1 -0
  72. package/dist/sqlite/runtime.d.ts +4 -0
  73. package/dist/sqlite/runtime.d.ts.map +1 -0
  74. package/dist/sqlite/runtime.js +34 -0
  75. package/dist/sqlite/runtime.js.map +1 -0
  76. package/dist/testing.d.ts +2 -0
  77. package/dist/testing.d.ts.map +1 -0
  78. package/dist/testing.js +2 -0
  79. package/dist/testing.js.map +1 -0
  80. package/dist/transaction/apply.d.ts +8 -0
  81. package/dist/transaction/apply.d.ts.map +1 -0
  82. package/dist/transaction/apply.js +273 -0
  83. package/dist/transaction/apply.js.map +1 -0
  84. package/dist/transaction/journal.d.ts +17 -0
  85. package/dist/transaction/journal.d.ts.map +1 -0
  86. package/dist/transaction/journal.js +207 -0
  87. package/dist/transaction/journal.js.map +1 -0
  88. package/dist/transaction/recovery.d.ts +6 -0
  89. package/dist/transaction/recovery.d.ts.map +1 -0
  90. package/dist/transaction/recovery.js +138 -0
  91. package/dist/transaction/recovery.js.map +1 -0
  92. package/dist/transaction/types.d.ts +45 -0
  93. package/dist/transaction/types.d.ts.map +1 -0
  94. package/dist/transaction/types.js +2 -0
  95. package/dist/transaction/types.js.map +1 -0
  96. package/native/rename_noreplace.cc +145 -0
  97. package/package.json +71 -0
  98. package/scripts/install-native.mjs +10 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dragos Cirjan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @neottia/repository-store
2
+
3
+ Hardened, domain-neutral persistence for canonical files and disposable SQLite caches inside a repository.
4
+
5
+ ```sh
6
+ pnpm add @neottia/repository-store
7
+ ```
8
+
9
+ Read the [complete Repository Store reference](https://github.com/dragoscirjan/neottia/blob/main/docs/repository-store.md).
10
+
11
+ The package owns byte-safe paths, SHA-256 revisions, one cross-process authority lease, durable rollback journals, atomic publication, and Node/Bun SQLite adapters. It deliberately does **not** define YAML or Markdown codecs, record schemas, search text, SQLite DDL, or query languages. Domain packages validate their complete proposed state while holding the lease, then publish exact bytes.
12
+
13
+ ## Requirements
14
+
15
+ - Node.js 22.16 or newer, using `node:sqlite`.
16
+ - Bun 1.3.13 or newer, using `bun:sqlite`.
17
+ - Linux x64 or arm64 with libc/kernel support for `renameat2(..., RENAME_NOREPLACE)`.
18
+ - A C++17 compiler, Python, Make, and Linux development headers so the Node-API addon can be built during installation.
19
+ - A recognized local ext4, XFS, Btrfs, tmpfs, or overlay filesystem with same-volume regular-file hard links.
20
+
21
+ The addon is built from architecture-neutral source and is loaded lazily by both Node and Bun. Repository authority acquisition preflights the addon and authority filesystem before creating claim or lease artifacts. macOS, Windows, missing or unloadable addon builds, kernels/libcs without `renameat2`, unrecognized filesystems, and shared/network filesystems return `UNSUPPORTED_RUNTIME`; leased canonical and cache operations, including read-only operations, are unavailable there.
22
+
23
+ ## Canonical files
24
+
25
+ ```ts
26
+ import {
27
+ DEFAULT_STORE_LIMITS,
28
+ applyCanonicalBatch,
29
+ readManagedFile,
30
+ resolveManagedPath,
31
+ resolveManagedRoot,
32
+ withRepositoryLease,
33
+ } from "@neottia/repository-store";
34
+
35
+ const root = await resolveManagedRoot({
36
+ authorityRoot: process.cwd(),
37
+ managedPath: ".neottia/example",
38
+ limits: DEFAULT_STORE_LIMITS,
39
+ });
40
+ const document = resolveManagedPath(root, "records/example.json");
41
+
42
+ await withRepositoryLease(root, async (lease) => {
43
+ await applyCanonicalBatch(root, lease, [
44
+ {
45
+ kind: "write",
46
+ path: document,
47
+ bytes: new TextEncoder().encode('{"value":1}\n'),
48
+ expected: "absent",
49
+ },
50
+ ]);
51
+ const current = await readManagedFile(root, lease, document);
52
+ console.log(current.revision);
53
+ });
54
+ ```
55
+
56
+ Every write, remove, and move has an exact expected revision. Paths reject traversal, ambiguous separators, control characters, Windows-invalid characters and device names, components ending in a period or space, portable case/NFKC collisions, symbolic links, special files, and multiply linked files. Reads compare descriptor and path identity plus size, modification time, and change time around bounded reads.
57
+
58
+ ## Lease and recovery
59
+
60
+ One lease lives at `<authority>/.neottia/repository-store/authority.lease`. Native no-replace cleanup capability is required before its claim or lease is created. It covers canonical reads, validation, mutation, recovery, and cache work. True nested same-authority acquisition in one active async call chain is rejected; independent same-process callers are queued FIFO with the same abort, deadline, and acquisition-timeout budget used for cross-process contention. Owner and host metadata are read through fixed 4 KiB, no-follow descriptors with path and ancestor identity checks. Leases include a random owner token and local host/process evidence and reclaim only stale owners whose same-host death is conclusive.
61
+
62
+ Before publication, bounded before-images, staged bytes, and a digested manifest are synchronized beneath `transactions/<managed-root-id>/`. Segregating journals prevents one domain subtree from interpreting another subtree's relative paths while all domains still share the authority lease. An `.active` journal is rollback-required; `.committed` marks the commit point, and `.cleanup` is restart-idempotent cleanup-only state. Recovery runs automatically after acquiring the lease. Ambiguous operator changes fail closed and preserve evidence.
63
+
64
+ ## Disposable SQLite caches
65
+
66
+ `openDisposableSqliteCache`, `rebuildDisposableSqliteCache`, and `removeDisposableSqliteCache` manage exact DB/WAL/SHM artifacts. Cache replacement tokens and allowed revisions are synchronized beneath `cache-publications/<managed-root-id>/`, so the next cache operation restores an evacuated active database after a crash before rebuilding. Runtime selection is lazy and loads only `node:sqlite` or `bun:sqlite`. Every connection enforces root SQL-byte, aggregate statement-parameter-byte, query-row, and query-result ceilings; `get()` is bounded automatically, `all()` cannot raise the root limits, aggregate schema SQL is bounded, and both active and candidate DB/WAL/SHM bytes share `maxTemporaryBytes`. Drivers must expose cursor iteration because eager unbounded `all()` fallback is rejected. Canonical files always win; a cache failure never rolls them back.
67
+
68
+ ## Durability and threat model
69
+
70
+ Publication synchronizes a private same-directory temporary file, evacuates an expected destination with native `RENAME_NOREPLACE`, validates its exact identity and revision, and creates the new name exclusively with regular-file hard-link semantics. A raced evacuation or replacement is never overwritten: it is restored exclusively or retained as actionable recovery evidence. There is no check-then-rename fallback. File and directory synchronization failures are `DurabilityError`s with `operation`, `path`, and the original `cause`.
71
+
72
+ Package-owned files use mode `0600` and directories use `0700` where supported. Near-match and malformed unowned artifacts are preserved rather than recursively deleted.
73
+
74
+ ## License
75
+
76
+ MIT
package/binding.gyp ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "repository_store_native",
5
+ "sources": ["native/rename_noreplace.cc"],
6
+ "cflags_cc": ["-std=c++17"]
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,43 @@
1
+ export type RepositoryStoreErrorCategory = 'config' | 'path_safety' | 'resource_limit' | 'stale_revision' | 'contention' | 'durability' | 'recovery' | 'cache_sync';
2
+ export type RepositoryStoreErrorCode = 'AUTHORITY_MISMATCH' | 'PATH_INVALID' | 'PATH_COLLISION' | 'UNSAFE_LINK' | 'UNSAFE_HARD_LINK' | 'IDENTITY_CHANGED' | 'LIMIT_EXCEEDED' | 'REVISION_MISMATCH' | 'LEASE_BUSY' | 'LEASE_REENTRANT' | 'LEASE_OWNER_UNKNOWN' | 'ABORTED' | 'DEADLINE_EXCEEDED' | 'FSYNC_FAILED' | 'RECOVERY_MALFORMED' | 'RECOVERY_AMBIGUOUS' | 'CACHE_BUSY' | 'CACHE_UNSAFE' | 'CACHE_SYNC_FAILED' | 'UNSUPPORTED_RUNTIME';
3
+ export declare class RepositoryStoreError extends Error {
4
+ readonly category: RepositoryStoreErrorCategory;
5
+ readonly code: RepositoryStoreErrorCode;
6
+ readonly retryable: boolean;
7
+ readonly evidence?: Readonly<Record<string, unknown>> | undefined;
8
+ constructor(message: string, category: RepositoryStoreErrorCategory, code: RepositoryStoreErrorCode, retryable: boolean, evidence?: Readonly<Record<string, unknown>> | undefined, options?: ErrorOptions);
9
+ }
10
+ export declare class RepositoryStoreConfigError extends RepositoryStoreError {
11
+ constructor(message: string, code?: RepositoryStoreErrorCode);
12
+ }
13
+ export declare class PathSafetyError extends RepositoryStoreError {
14
+ constructor(message: string, code?: RepositoryStoreErrorCode, evidence?: Readonly<Record<string, unknown>>);
15
+ }
16
+ export declare class ResourceLimitError extends RepositoryStoreError {
17
+ constructor(message: string, evidence?: Readonly<Record<string, unknown>>);
18
+ }
19
+ export declare class StaleRevisionError extends RepositoryStoreError {
20
+ constructor(message: string, evidence?: Readonly<Record<string, unknown>>);
21
+ }
22
+ export declare class LeaseContentionError extends RepositoryStoreError {
23
+ constructor(message: string, code?: RepositoryStoreErrorCode);
24
+ }
25
+ export interface DurabilityEvidence extends Readonly<Record<string, unknown>> {
26
+ readonly operation: 'file-fsync' | 'directory-fsync';
27
+ readonly path: string;
28
+ }
29
+ export declare class DurabilityError extends RepositoryStoreError {
30
+ constructor(message: string, options?: ErrorOptions & {
31
+ readonly evidence?: DurabilityEvidence;
32
+ });
33
+ }
34
+ export declare class RecoveryError extends RepositoryStoreError {
35
+ constructor(message: string, code: 'RECOVERY_MALFORMED' | 'RECOVERY_AMBIGUOUS', evidence?: Readonly<Record<string, unknown>>, options?: ErrorOptions);
36
+ }
37
+ export declare class CacheSyncError extends RepositoryStoreError {
38
+ constructor(message: string, code?: RepositoryStoreErrorCode, options?: ErrorOptions);
39
+ }
40
+ export declare class UnsupportedRuntimeError extends RepositoryStoreError {
41
+ constructor(message: string);
42
+ }
43
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,4BAA4B,GACpC,QAAQ,GACR,aAAa,GACb,gBAAgB,GAChB,gBAAgB,GAChB,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,YAAY,CAAC;AAGjB,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,YAAY,GACZ,iBAAiB,GACjB,qBAAqB,GACrB,SAAS,GACT,mBAAmB,GACnB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,YAAY,GACZ,cAAc,GACd,mBAAmB,GACnB,qBAAqB,CAAC;AAG1B,qBAAa,oBAAqB,SAAQ,KAAK;aAG3B,QAAQ,EAAE,4BAA4B;aACtC,IAAI,EAAE,wBAAwB;aAC9B,SAAS,EAAE,OAAO;aAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAJ5D,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,4BAA4B,EACtC,IAAI,EAAE,wBAAwB,EAC9B,SAAS,EAAE,OAAO,EAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,YAAA,EAC5D,OAAO,CAAC,EAAE,YAAY;CAKzB;AAGD,qBAAa,0BAA2B,SAAQ,oBAAoB;gBAC/C,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,wBAAyC;CAIpF;AAGD,qBAAa,eAAgB,SAAQ,oBAAoB;gBAErD,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,wBAAyC,EAC/C,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAK/C;AAGD,qBAAa,kBAAmB,SAAQ,oBAAoB;gBACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAIjF;AAGD,qBAAa,kBAAmB,SAAQ,oBAAoB;gBACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAIjF;AAGD,qBAAa,oBAAqB,SAAQ,oBAAoB;gBACzC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,wBAAuC;CAIlF;AAGD,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3E,QAAQ,CAAC,SAAS,EAAE,YAAY,GAAG,iBAAiB,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAGD,qBAAa,eAAgB,SAAQ,oBAAoB;gBACpC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;KAAE;CAIxG;AAGD,qBAAa,aAAc,SAAQ,oBAAoB;gBAEnD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,oBAAoB,GAAG,oBAAoB,EACjD,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5C,OAAO,CAAC,EAAE,YAAY;CAKzB;AAGD,qBAAa,cAAe,SAAQ,oBAAoB;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,wBAA8C,EAAE,OAAO,CAAC,EAAE,YAAY;CAIjH;AAGD,qBAAa,uBAAwB,SAAQ,oBAAoB;gBAC5C,OAAO,EAAE,MAAM;CAInC"}
package/dist/errors.js ADDED
@@ -0,0 +1,69 @@
1
+ export class RepositoryStoreError extends Error {
2
+ category;
3
+ code;
4
+ retryable;
5
+ evidence;
6
+ constructor(message, category, code, retryable, evidence, options) {
7
+ super(message, options);
8
+ this.category = category;
9
+ this.code = code;
10
+ this.retryable = retryable;
11
+ this.evidence = evidence;
12
+ this.name = 'RepositoryStoreError';
13
+ }
14
+ }
15
+ export class RepositoryStoreConfigError extends RepositoryStoreError {
16
+ constructor(message, code = 'PATH_INVALID') {
17
+ super(message, 'config', code, false);
18
+ this.name = 'RepositoryStoreConfigError';
19
+ }
20
+ }
21
+ export class PathSafetyError extends RepositoryStoreError {
22
+ constructor(message, code = 'PATH_INVALID', evidence) {
23
+ super(message, 'path_safety', code, false, evidence);
24
+ this.name = 'PathSafetyError';
25
+ }
26
+ }
27
+ export class ResourceLimitError extends RepositoryStoreError {
28
+ constructor(message, evidence) {
29
+ super(message, 'resource_limit', 'LIMIT_EXCEEDED', false, evidence);
30
+ this.name = 'ResourceLimitError';
31
+ }
32
+ }
33
+ export class StaleRevisionError extends RepositoryStoreError {
34
+ constructor(message, evidence) {
35
+ super(message, 'stale_revision', 'REVISION_MISMATCH', true, evidence);
36
+ this.name = 'StaleRevisionError';
37
+ }
38
+ }
39
+ export class LeaseContentionError extends RepositoryStoreError {
40
+ constructor(message, code = 'LEASE_BUSY') {
41
+ super(message, 'contention', code, true);
42
+ this.name = 'LeaseContentionError';
43
+ }
44
+ }
45
+ export class DurabilityError extends RepositoryStoreError {
46
+ constructor(message, options) {
47
+ super(message, 'durability', 'FSYNC_FAILED', false, options?.evidence, options);
48
+ this.name = 'DurabilityError';
49
+ }
50
+ }
51
+ export class RecoveryError extends RepositoryStoreError {
52
+ constructor(message, code, evidence, options) {
53
+ super(message, 'recovery', code, false, evidence, options);
54
+ this.name = 'RecoveryError';
55
+ }
56
+ }
57
+ export class CacheSyncError extends RepositoryStoreError {
58
+ constructor(message, code = 'CACHE_SYNC_FAILED', options) {
59
+ super(message, 'cache_sync', code, code === 'CACHE_BUSY', undefined, options);
60
+ this.name = 'CacheSyncError';
61
+ }
62
+ }
63
+ export class UnsupportedRuntimeError extends RepositoryStoreError {
64
+ constructor(message) {
65
+ super(message, 'config', 'UNSUPPORTED_RUNTIME', false);
66
+ this.name = 'UnsupportedRuntimeError';
67
+ }
68
+ }
69
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { type ManagedPath, type ManagedRoot, type RepositoryLease } from './internal/model.js';
2
+ import { type OperationControl } from './lease.js';
3
+ import { type ByteRevision } from './revision.js';
4
+ export interface ManagedFile {
5
+ readonly path: ManagedPath;
6
+ readonly bytes: Uint8Array;
7
+ readonly revision: ByteRevision;
8
+ readonly size: number;
9
+ readonly mtimeMs: number;
10
+ }
11
+ export interface ScanManagedFilesOptions extends OperationControl {
12
+ readonly under: readonly ManagedPath[];
13
+ readonly accept?: (relativePath: string) => boolean;
14
+ }
15
+ export declare function scanManagedFiles(root: ManagedRoot, lease: RepositoryLease, options: ScanManagedFilesOptions): Promise<readonly ManagedFile[]>;
16
+ export declare function readManagedFile(root: ManagedRoot, lease: RepositoryLease, path: ManagedPath, options?: OperationControl): Promise<ManagedFile>;
17
+ export declare function readManagedFileIfExists(root: ManagedRoot, lease: RepositoryLease, path: ManagedPath, options?: OperationControl): Promise<ManagedFile | undefined>;
18
+ export declare function assertPathAuthority(root: ManagedRoot, path: ManagedPath): void;
19
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAiC,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAElF,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAGvE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAGD,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAC/D,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CACrD;AAGD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAgCjC;AAGD,wBAAsB,eAAe,CACnC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,CAAC,CActB;AAGD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAOlC;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAG9E"}
package/dist/files.js ADDED
@@ -0,0 +1,113 @@
1
+ import { lstatSync, readdirSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+ import { PathSafetyError, ResourceLimitError } from './errors.js';
4
+ import { assertSafeRegular, captureDirectories, hasCode, readRegularFile, revalidateDirectories, } from './internal/filesystem.js';
5
+ import { getPathState, getRootState, managedPathBelongsToRoot, } from './internal/model.js';
6
+ import { assertLiveLease, checkControl } from './lease.js';
7
+ import { portablePathKey, resolveManagedPath } from './paths.js';
8
+ import { computeByteRevision } from './revision.js';
9
+ export async function scanManagedFiles(root, lease, options) {
10
+ assertLiveLease(root, lease);
11
+ checkControl(options);
12
+ const rootState = getRootState(root);
13
+ if (rootState === undefined)
14
+ throw new PathSafetyError('Managed root is not a repository-store handle.');
15
+ const discovered = [];
16
+ for (const start of options.under) {
17
+ assertPathAuthority(root, start);
18
+ const startState = getPathState(start);
19
+ if (startState === undefined)
20
+ throw new PathSafetyError('Managed path is not a repository-store handle.');
21
+ walk(root, startState.absolutePath, discovered, options.accept, options, rootState.limits.maxFiles);
22
+ }
23
+ const keyed = new Map();
24
+ for (const path of discovered) {
25
+ const key = portablePathKey(path.relativePath);
26
+ const previous = keyed.get(key);
27
+ if (previous !== undefined && previous !== path.relativePath)
28
+ throw new PathSafetyError(`Portable path collision: ${previous} and ${path.relativePath}`, 'PATH_COLLISION');
29
+ keyed.set(key, path.relativePath);
30
+ }
31
+ if (discovered.length > rootState.limits.maxFiles)
32
+ throw new ResourceLimitError('Managed file count limit exceeded.');
33
+ const results = [];
34
+ let totalBytes = 0;
35
+ for (const path of discovered.sort((left, right) => left.relativePath.localeCompare(right.relativePath))) {
36
+ checkControl(options);
37
+ const file = await readManagedFile(root, lease, path, options);
38
+ totalBytes += file.size;
39
+ if (totalBytes > rootState.limits.maxTotalBytes)
40
+ throw new ResourceLimitError('Managed aggregate byte limit exceeded.');
41
+ results.push(file);
42
+ }
43
+ return results;
44
+ }
45
+ export async function readManagedFile(root, lease, path, options = {}) {
46
+ assertLiveLease(root, lease);
47
+ assertPathAuthority(root, path);
48
+ checkControl(options);
49
+ const rootState = getRootState(root);
50
+ const pathState = getPathState(path);
51
+ if (rootState === undefined || pathState === undefined)
52
+ throw new PathSafetyError('Managed root or path is not a repository-store handle.');
53
+ const absolutePath = pathState.absolutePath;
54
+ const bytes = readRegularFile(absolutePath, rootState.limits.maxFileBytes);
55
+ const stat = lstatSync(absolutePath);
56
+ assertSafeRegular(stat, absolutePath);
57
+ checkControl(options);
58
+ return { path, bytes, revision: computeByteRevision(bytes), size: bytes.byteLength, mtimeMs: stat.mtimeMs };
59
+ }
60
+ export async function readManagedFileIfExists(root, lease, path, options = {}) {
61
+ try {
62
+ return await readManagedFile(root, lease, path, options);
63
+ }
64
+ catch (error) {
65
+ if (hasCode(error, 'ENOENT'))
66
+ return undefined;
67
+ throw error;
68
+ }
69
+ }
70
+ export function assertPathAuthority(root, path) {
71
+ if (!managedPathBelongsToRoot(root, path))
72
+ throw new PathSafetyError('Managed path belongs to a different managed root.', 'PATH_INVALID');
73
+ }
74
+ function walk(root, absolute, output, accept, control, maxFiles) {
75
+ checkControl(control);
76
+ const rootState = getRootState(root);
77
+ if (rootState === undefined)
78
+ throw new PathSafetyError('Managed root is not a repository-store handle.');
79
+ let stat;
80
+ try {
81
+ stat = lstatSync(absolute);
82
+ }
83
+ catch (error) {
84
+ if (hasCode(error, 'ENOENT'))
85
+ return;
86
+ throw error;
87
+ }
88
+ if (stat.isSymbolicLink())
89
+ throw new PathSafetyError(`Symbolic link is not allowed: ${absolute}`, 'UNSAFE_LINK');
90
+ if (stat.isFile()) {
91
+ assertSafeRegular(stat, absolute);
92
+ const path = relative(rootState.managedRoot, absolute).split('\\').join('/');
93
+ if (accept?.(path) !== false) {
94
+ output.push(resolveManagedPath(root, path));
95
+ if (output.length > maxFiles)
96
+ throw new ResourceLimitError('Managed file count limit exceeded.');
97
+ }
98
+ return;
99
+ }
100
+ if (!stat.isDirectory())
101
+ throw new PathSafetyError(`Special managed path is not allowed: ${absolute}`, 'UNSAFE_LINK');
102
+ const identities = captureDirectories(absolute);
103
+ const entries = readdirSync(absolute, { withFileTypes: true });
104
+ revalidateDirectories(identities);
105
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
106
+ checkControl(control);
107
+ if (entry.isSymbolicLink())
108
+ throw new PathSafetyError(`Symbolic link is not allowed: ${join(absolute, entry.name)}`, 'UNSAFE_LINK');
109
+ walk(root, join(absolute, entry.name), output, accept, control, maxFiles);
110
+ }
111
+ revalidateDirectories(identities);
112
+ }
113
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export { CacheSyncError, DurabilityError, LeaseContentionError, PathSafetyError, RecoveryError, RepositoryStoreConfigError, RepositoryStoreError, ResourceLimitError, StaleRevisionError, UnsupportedRuntimeError, type RepositoryStoreErrorCategory, type RepositoryStoreErrorCode, } from './errors.js';
2
+ export { readManagedFile, scanManagedFiles, type ManagedFile, type ScanManagedFilesOptions } from './files.js';
3
+ export { withRepositoryLease, type OperationControl, type RepositoryLease, type RepositoryLeaseOptions, } from './lease.js';
4
+ export { DEFAULT_STORE_LIMITS, type StoreLimits } from './limits.js';
5
+ export { PORTABLE_RELATIVE_PATH_PATTERN, resolveManagedPath, resolveManagedRoot, validateRelativePath, type ManagedPath, type ManagedRoot, type ManagedRootOptions, } from './paths.js';
6
+ export { computeByteRevision, type ByteRevision } from './revision.js';
7
+ export { openDisposableSqliteCache, rebuildDisposableSqliteCache, removeDisposableSqliteCache, } from './sqlite/cache.js';
8
+ export type { CacheOpenResult, CacheRebuildReason, DisposableCacheSpecification, DisposableSqliteCache, } from './sqlite/cache.js';
9
+ export { selectSqliteAdapter } from './sqlite/runtime.js';
10
+ export type { SqliteAdapter, SqliteConnection, SqliteConnectionBounds, SqliteParameters, SqliteRunResult, SqliteStatement, SqliteValue, } from './sqlite/adapter.js';
11
+ export { applyCanonicalBatch } from './transaction/apply.js';
12
+ export { recoverCanonicalTransactions } from './transaction/recovery.js';
13
+ export type { ApplyCanonicalBatchOptions, CanonicalOperation, RecoveryReport } from './transaction/types.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,4BAA4B,EACjC,KAAK,wBAAwB,GAC9B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,8BAA8B,EAC9B,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzE,YAAY,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { CacheSyncError, DurabilityError, LeaseContentionError, PathSafetyError, RecoveryError, RepositoryStoreConfigError, RepositoryStoreError, ResourceLimitError, StaleRevisionError, UnsupportedRuntimeError, } from './errors.js';
2
+ export { readManagedFile, scanManagedFiles } from './files.js';
3
+ export { withRepositoryLease, } from './lease.js';
4
+ export { DEFAULT_STORE_LIMITS } from './limits.js';
5
+ export { PORTABLE_RELATIVE_PATH_PATTERN, resolveManagedPath, resolveManagedRoot, validateRelativePath, } from './paths.js';
6
+ export { computeByteRevision } from './revision.js';
7
+ export { openDisposableSqliteCache, rebuildDisposableSqliteCache, removeDisposableSqliteCache, } from './sqlite/cache.js';
8
+ export { selectSqliteAdapter } from './sqlite/runtime.js';
9
+ export { applyCanonicalBatch } from './transaction/apply.js';
10
+ export { recoverCanonicalTransactions } from './transaction/recovery.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GAGxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAkD,MAAM,YAAY,CAAC;AAC/G,OAAO,EACL,mBAAmB,GAIpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAoB,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,8BAA8B,EAC9B,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GAIrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAqB,MAAM,eAAe,CAAC;AACvE,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAU1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const TRANSACTION_FAULT_EVENTS: readonly ["prepare-directory-created", "before-image-written", "staged-artifact-written", "manifest-written", "prepare-directory-synced", "active-state-renamed", "active-state-synced", "canonical-path-published", "committed-state-renamed", "committed-state-synced", "cleanup-state-renamed", "cleanup-state-synced", "cleanup-artifact-removed", "cleanup-manifest-removed", "cleanup-directory-removed", "cleanup-root-synced"];
2
+ export type TransactionFaultEvent = (typeof TRANSACTION_FAULT_EVENTS)[number];
3
+ export declare const LEASE_FAULT_EVENTS: readonly ["claim-published", "claim-finalized", "lease-directory-created", "lease-owner-written", "lease-claim-removed"];
4
+ export type LeaseFaultEvent = (typeof LEASE_FAULT_EVENTS)[number];
5
+ export declare const FILESYSTEM_FAULT_EVENTS: readonly ["bounded-read-opened", "before-exact-publication", "before-exact-removal", "before-exact-evacuation", "destination-evacuated", "exclusive-destination-published", "before-lease-artifact-quarantine", "after-stale-claim-owner-verified", "file-fsync", "directory-fsync"];
6
+ export type FilesystemFaultEvent = (typeof FILESYSTEM_FAULT_EVENTS)[number];
7
+ export declare function setTransactionFaultInjectorForTests(injector: ((event: TransactionFaultEvent, occurrence: number) => void) | undefined): void;
8
+ export declare function emitTransactionFault(event: TransactionFaultEvent): void;
9
+ export declare function setLeaseFaultInjectorForTests(injector: ((event: LeaseFaultEvent) => void) | undefined): void;
10
+ export declare function emitLeaseFault(event: LeaseFaultEvent): void;
11
+ export declare function setFilesystemFaultInjectorForTests(injector: ((event: FilesystemFaultEvent, path: string, occurrence: number) => void) | undefined): void;
12
+ export declare function emitFilesystemFault(event: FilesystemFaultEvent, path: string): void;
13
+ //# sourceMappingURL=fault-injection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-injection.d.ts","sourceRoot":"","sources":["../../src/internal/fault-injection.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,wBAAwB,waAiB3B,CAAC;AAGX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAG9E,eAAO,MAAM,kBAAkB,0HAMrB,CAAC;AAGX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAGlE,eAAO,MAAM,uBAAuB,sRAW1B,CAAC;AAGX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAS5E,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GACjF,IAAI,CAGN;AAGD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAIvE;AAGD,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAE5G;AAGD,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAE3D;AAGD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GAC9F,IAAI,CAGN;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAKnF"}
@@ -0,0 +1,68 @@
1
+ export const TRANSACTION_FAULT_EVENTS = [
2
+ 'prepare-directory-created',
3
+ 'before-image-written',
4
+ 'staged-artifact-written',
5
+ 'manifest-written',
6
+ 'prepare-directory-synced',
7
+ 'active-state-renamed',
8
+ 'active-state-synced',
9
+ 'canonical-path-published',
10
+ 'committed-state-renamed',
11
+ 'committed-state-synced',
12
+ 'cleanup-state-renamed',
13
+ 'cleanup-state-synced',
14
+ 'cleanup-artifact-removed',
15
+ 'cleanup-manifest-removed',
16
+ 'cleanup-directory-removed',
17
+ 'cleanup-root-synced',
18
+ ];
19
+ export const LEASE_FAULT_EVENTS = [
20
+ 'claim-published',
21
+ 'claim-finalized',
22
+ 'lease-directory-created',
23
+ 'lease-owner-written',
24
+ 'lease-claim-removed',
25
+ ];
26
+ export const FILESYSTEM_FAULT_EVENTS = [
27
+ 'bounded-read-opened',
28
+ 'before-exact-publication',
29
+ 'before-exact-removal',
30
+ 'before-exact-evacuation',
31
+ 'destination-evacuated',
32
+ 'exclusive-destination-published',
33
+ 'before-lease-artifact-quarantine',
34
+ 'after-stale-claim-owner-verified',
35
+ 'file-fsync',
36
+ 'directory-fsync',
37
+ ];
38
+ let transactionFaultInjector;
39
+ let leaseFaultInjector;
40
+ let filesystemFaultInjector;
41
+ const transactionOccurrences = new Map();
42
+ const filesystemOccurrences = new Map();
43
+ export function setTransactionFaultInjectorForTests(injector) {
44
+ transactionOccurrences.clear();
45
+ transactionFaultInjector = injector;
46
+ }
47
+ export function emitTransactionFault(event) {
48
+ const occurrence = (transactionOccurrences.get(event) ?? 0) + 1;
49
+ transactionOccurrences.set(event, occurrence);
50
+ transactionFaultInjector?.(event, occurrence);
51
+ }
52
+ export function setLeaseFaultInjectorForTests(injector) {
53
+ leaseFaultInjector = injector;
54
+ }
55
+ export function emitLeaseFault(event) {
56
+ leaseFaultInjector?.(event);
57
+ }
58
+ export function setFilesystemFaultInjectorForTests(injector) {
59
+ filesystemOccurrences.clear();
60
+ filesystemFaultInjector = injector;
61
+ }
62
+ export function emitFilesystemFault(event, path) {
63
+ const key = `${event}\0${path}`;
64
+ const occurrence = (filesystemOccurrences.get(key) ?? 0) + 1;
65
+ filesystemOccurrences.set(key, occurrence);
66
+ filesystemFaultInjector?.(event, path, occurrence);
67
+ }
68
+ //# sourceMappingURL=fault-injection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-injection.js","sourceRoot":"","sources":["../../src/internal/fault-injection.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,2BAA2B;IAC3B,sBAAsB;IACtB,yBAAyB;IACzB,kBAAkB;IAClB,0BAA0B;IAC1B,sBAAsB;IACtB,qBAAqB;IACrB,0BAA0B;IAC1B,yBAAyB;IACzB,wBAAwB;IACxB,uBAAuB;IACvB,sBAAsB;IACtB,0BAA0B;IAC1B,0BAA0B;IAC1B,2BAA2B;IAC3B,qBAAqB;CACb,CAAC;AAMX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,iBAAiB;IACjB,iBAAiB;IACjB,yBAAyB;IACzB,qBAAqB;IACrB,qBAAqB;CACb,CAAC;AAMX,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,qBAAqB;IACrB,0BAA0B;IAC1B,sBAAsB;IACtB,yBAAyB;IACzB,uBAAuB;IACvB,iCAAiC;IACjC,kCAAkC;IAClC,kCAAkC;IAClC,YAAY;IACZ,iBAAiB;CACT,CAAC;AAKX,IAAI,wBAAkG,CAAC;AACvG,IAAI,kBAAkE,CAAC;AACvE,IAAI,uBAA8G,CAAC;AACnH,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAiC,CAAC;AACxE,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAGxD,MAAM,UAAU,mCAAmC,CACjD,QAAkF;IAElF,sBAAsB,CAAC,KAAK,EAAE,CAAC;IAC/B,wBAAwB,GAAG,QAAQ,CAAC;AACtC,CAAC;AAGD,MAAM,UAAU,oBAAoB,CAAC,KAA4B;IAC/D,MAAM,UAAU,GAAG,CAAC,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAChE,sBAAsB,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC9C,wBAAwB,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAGD,MAAM,UAAU,6BAA6B,CAAC,QAAwD;IACpG,kBAAkB,GAAG,QAAQ,CAAC;AAChC,CAAC;AAGD,MAAM,UAAU,cAAc,CAAC,KAAsB;IACnD,kBAAkB,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAGD,MAAM,UAAU,kCAAkC,CAChD,QAA+F;IAE/F,qBAAqB,CAAC,KAAK,EAAE,CAAC;IAC9B,uBAAuB,GAAG,QAAQ,CAAC;AACrC,CAAC;AAGD,MAAM,UAAU,mBAAmB,CAAC,KAA2B,EAAE,IAAY;IAC3E,MAAM,GAAG,GAAG,GAAG,KAAK,KAAK,IAAI,EAAE,CAAC;IAChC,MAAM,UAAU,GAAG,CAAC,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7D,qBAAqB,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC3C,uBAAuB,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type Stats } from 'node:fs';
2
+ export interface DirectoryIdentity {
3
+ readonly path: string;
4
+ readonly stat: Stats;
5
+ }
6
+ export declare function noFollowFlag(): number;
7
+ export declare function hasCode(error: unknown, code: string): boolean;
8
+ export declare function sameIdentity(left: Stats, right: Stats): boolean;
9
+ export declare function ensurePrivateDirectory(target: string): void;
10
+ export declare function captureDirectories(directory: string): DirectoryIdentity[];
11
+ export declare function revalidateDirectories(identities: readonly DirectoryIdentity[]): void;
12
+ export declare function readRegularFile(path: string, maxBytes: number, options?: {
13
+ readonly allowedLinkCounts?: readonly number[];
14
+ }): Uint8Array;
15
+ export declare function readRegularFileWithIdentity(path: string, maxBytes: number, options?: {
16
+ readonly allowedLinkCounts?: readonly number[];
17
+ }): {
18
+ readonly bytes: Uint8Array;
19
+ readonly identity: Stats;
20
+ };
21
+ export declare function assertSafeRegular(stat: Stats, path: string): void;
22
+ export declare function syncFileDescriptor(descriptor: number, path: string): void;
23
+ export declare function syncRegularFile(path: string): void;
24
+ export declare function syncDirectory(path: string): void;
25
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/internal/filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,KAAK,EACX,MAAM,SAAS,CAAC;AAMjB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACtB;AAGD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAGD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAE/D;AAGD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA0B3D;AAGD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASzE;AAQD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI,CAWpF;AAGD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC/D,UAAU,CAEZ;AAGD,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAO,GAC/D;IAAE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAA;CAAE,CA6C1D;AAGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjE;AAUD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAUzE;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAalD;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBhD"}