@coderifts/agent-guard 6.1.0 → 6.3.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 (66) hide show
  1. package/README.md +68 -2
  2. package/dist/cjs/cas-adapters/api.d.ts +94 -0
  3. package/dist/cjs/cas-adapters/api.d.ts.map +1 -0
  4. package/dist/cjs/cas-adapters/api.js +120 -0
  5. package/dist/cjs/cas-adapters/api.js.map +1 -0
  6. package/dist/cjs/cas-adapters/db.d.ts +93 -0
  7. package/dist/cjs/cas-adapters/db.d.ts.map +1 -0
  8. package/dist/cjs/cas-adapters/db.js +139 -0
  9. package/dist/cjs/cas-adapters/db.js.map +1 -0
  10. package/dist/cjs/cas-adapters/fs.d.ts +79 -0
  11. package/dist/cjs/cas-adapters/fs.d.ts.map +1 -0
  12. package/dist/cjs/cas-adapters/fs.js +224 -0
  13. package/dist/cjs/cas-adapters/fs.js.map +1 -0
  14. package/dist/cjs/cas-adapters/registry.d.ts +88 -0
  15. package/dist/cjs/cas-adapters/registry.d.ts.map +1 -0
  16. package/dist/cjs/cas-adapters/registry.js +120 -0
  17. package/dist/cjs/cas-adapters/registry.js.map +1 -0
  18. package/dist/cjs/cas-attestation.d.ts +94 -0
  19. package/dist/cjs/cas-attestation.d.ts.map +1 -0
  20. package/dist/cjs/cas-attestation.js +133 -0
  21. package/dist/cjs/cas-attestation.js.map +1 -0
  22. package/dist/cjs/conditional-write.d.ts +66 -0
  23. package/dist/cjs/conditional-write.d.ts.map +1 -1
  24. package/dist/cjs/conditional-write.js +72 -1
  25. package/dist/cjs/conditional-write.js.map +1 -1
  26. package/dist/cjs/index.d.ts +11 -2
  27. package/dist/cjs/index.d.ts.map +1 -1
  28. package/dist/cjs/index.js +44 -3
  29. package/dist/cjs/index.js.map +1 -1
  30. package/dist/cjs/with-coderifts.d.ts +8 -0
  31. package/dist/cjs/with-coderifts.d.ts.map +1 -1
  32. package/dist/cjs/with-coderifts.js +3 -0
  33. package/dist/cjs/with-coderifts.js.map +1 -1
  34. package/dist/esm/cas-adapters/api.d.ts +94 -0
  35. package/dist/esm/cas-adapters/api.d.ts.map +1 -0
  36. package/dist/esm/cas-adapters/api.js +116 -0
  37. package/dist/esm/cas-adapters/api.js.map +1 -0
  38. package/dist/esm/cas-adapters/db.d.ts +93 -0
  39. package/dist/esm/cas-adapters/db.d.ts.map +1 -0
  40. package/dist/esm/cas-adapters/db.js +135 -0
  41. package/dist/esm/cas-adapters/db.js.map +1 -0
  42. package/dist/esm/cas-adapters/fs.d.ts +79 -0
  43. package/dist/esm/cas-adapters/fs.d.ts.map +1 -0
  44. package/dist/esm/cas-adapters/fs.js +185 -0
  45. package/dist/esm/cas-adapters/fs.js.map +1 -0
  46. package/dist/esm/cas-adapters/registry.d.ts +88 -0
  47. package/dist/esm/cas-adapters/registry.d.ts.map +1 -0
  48. package/dist/esm/cas-adapters/registry.js +116 -0
  49. package/dist/esm/cas-adapters/registry.js.map +1 -0
  50. package/dist/esm/cas-attestation.d.ts +94 -0
  51. package/dist/esm/cas-attestation.d.ts.map +1 -0
  52. package/dist/esm/cas-attestation.js +127 -0
  53. package/dist/esm/cas-attestation.js.map +1 -0
  54. package/dist/esm/conditional-write.d.ts +66 -0
  55. package/dist/esm/conditional-write.d.ts.map +1 -1
  56. package/dist/esm/conditional-write.js +69 -0
  57. package/dist/esm/conditional-write.js.map +1 -1
  58. package/dist/esm/index.d.ts +11 -2
  59. package/dist/esm/index.d.ts.map +1 -1
  60. package/dist/esm/index.js +14 -2
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/with-coderifts.d.ts +8 -0
  63. package/dist/esm/with-coderifts.d.ts.map +1 -1
  64. package/dist/esm/with-coderifts.js +3 -0
  65. package/dist/esm/with-coderifts.js.map +1 -1
  66. package/package.json +1 -1
@@ -0,0 +1,94 @@
1
+ /**
2
+ * HTTP/API CAS adapter — ETag (If-Match) token discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * api:v1:<raw_etag_or_version_header_value>
9
+ * Missing / empty ETag → API_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter does NOT perform HTTP. The host callback MUST send If-Match (or
12
+ * equivalent) itself. The adapter only normalizes tokens and maps the host-reported
13
+ * {committed|precondition_failed} onto ExecuteIfUnchangedOutcome. Same class as
14
+ * conditional_write_is_host_asserted_not_cas_verified — the package never claims it sent
15
+ * the header.
16
+ *
17
+ * No fetch/undici — all I/O is host-injected.
18
+ */
19
+ import { tokensEqual, type ExecuteIfUnchangedOutcome, type VersionToken } from '../conditional-write.js';
20
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
21
+ export declare const API_VERSION_TOKEN_PREFIX = "api:v1:";
22
+ /**
23
+ * Token used when the resource has no ETag / version header yet (or host reported null).
24
+ * Hosts that measured absence should pass this as expected_token for create-if-absent CAS.
25
+ */
26
+ export declare const API_ABSENT_TOKEN: VersionToken;
27
+ /**
28
+ * Build a VersionToken from a host-supplied ETag or version-header value.
29
+ * null / undefined / empty / whitespace-only → API_ABSENT_TOKEN.
30
+ * Surrounding weak/strong quote marks are stripped once for stable equality.
31
+ */
32
+ export declare function createApiVersionToken(etag: string | null | undefined): VersionToken;
33
+ /** Raw value embedded in an api:v1: token (for host If-Match headers). Absent → null. */
34
+ export declare function apiTokenRaw(token: VersionToken | null | undefined): string | null;
35
+ /**
36
+ * Host report after attempting a conditional write.
37
+ * The host is responsible for sending If-Match / If-None-Match / equivalent.
38
+ */
39
+ export type ApiHostWriteReport<T = unknown> = {
40
+ status: 'committed';
41
+ /** New ETag after write when the server returned one; null/omit when unknown. */
42
+ new_etag?: string | null;
43
+ result?: T;
44
+ } | {
45
+ status: 'precondition_failed';
46
+ /** Current ETag if the host observed one (e.g. from 412 body / re-GET); omit → null. */
47
+ current_etag?: string | null;
48
+ };
49
+ export type WriteApiIfUnchangedArgs<T = unknown> = {
50
+ /** Token measured before the host decided to write (from createApiVersionToken). */
51
+ expected_token: VersionToken;
52
+ /**
53
+ * Re-read the resource ETag (or equivalent version header).
54
+ * Called by executeIfUnchanged before write and again after write when detect is on.
55
+ */
56
+ current_etag: () => string | null | Promise<string | null>;
57
+ /**
58
+ * Host performs the conditional HTTP write.
59
+ * `if_match` is the raw expected ETag for the If-Match header (null when ABSENT).
60
+ * Adapter does not send the request — host must.
61
+ */
62
+ write: (ctx: {
63
+ if_match: string | null;
64
+ expected_token: VersionToken;
65
+ }) => ApiHostWriteReport<T> | Promise<ApiHostWriteReport<T>>;
66
+ /**
67
+ * Opt-in post-commit detection (default false — byte-identical when off).
68
+ * Requires a meaningful intended post-ETag: when the host omits new_etag on success,
69
+ * detection is a no-op (cannot know intended post state without inventing tokens).
70
+ */
71
+ detect_stale_during_commit?: boolean;
72
+ };
73
+ export type ApiWriteResult<T = unknown> = {
74
+ new_etag: string | null;
75
+ result: T | undefined;
76
+ };
77
+ /**
78
+ * Conditional API write: re-read ETag; if token matches expected → host write with If-Match context.
79
+ *
80
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
81
+ * committed (+ optional new_etag) → committed (version_token = expected; result carries new_etag)
82
+ * precondition_failed (412 class) → refused / stale_version_token
83
+ * current_token from current_etag when host supplied it,
84
+ * else null (no invented token)
85
+ *
86
+ * Missing new_etag on success: stored as null on result; detect_stale_during_commit is a no-op
87
+ * unless new_etag is a non-empty string (honest — no ABSENT fabrication as "expected after").
88
+ *
89
+ * Wired through executeIfUnchanged — not a parallel path.
90
+ */
91
+ export declare function writeApiIfUnchanged<T = unknown>(args: WriteApiIfUnchangedArgs<T>): Promise<ExecuteIfUnchangedOutcome<ApiWriteResult<T>>>;
92
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
93
+ export { tokensEqual };
94
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAA8B,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,YAAY,CAWd;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAKjF;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,OAAO,IACtC;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ,GACD;IACE,MAAM,EAAE,qBAAqB,CAAC;IAC9B,wFAAwF;IACxF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEN,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,OAAO,IAAI;IACjD,oFAAoF;IACpF,cAAc,EAAE,YAAY,CAAC;IAC7B;;;OAGG;IACH,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D;;;;OAIG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE;QACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,cAAc,EAAE,YAAY,CAAC;KAC9B,KAAK,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,OAAO,IAAI;IACxC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,GAAG,OAAO,EACnD,IAAI,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,yBAAyB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CA8CvD;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * HTTP/API CAS adapter — ETag (If-Match) token discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * api:v1:<raw_etag_or_version_header_value>
9
+ * Missing / empty ETag → API_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter does NOT perform HTTP. The host callback MUST send If-Match (or
12
+ * equivalent) itself. The adapter only normalizes tokens and maps the host-reported
13
+ * {committed|precondition_failed} onto ExecuteIfUnchangedOutcome. Same class as
14
+ * conditional_write_is_host_asserted_not_cas_verified — the package never claims it sent
15
+ * the header.
16
+ *
17
+ * No fetch/undici — all I/O is host-injected.
18
+ */
19
+ 'use strict';
20
+ import { executeIfUnchanged, StaleVersionTokenAbort, tokensEqual, } from '../conditional-write.js';
21
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
22
+ export const API_VERSION_TOKEN_PREFIX = 'api:v1:';
23
+ /**
24
+ * Token used when the resource has no ETag / version header yet (or host reported null).
25
+ * Hosts that measured absence should pass this as expected_token for create-if-absent CAS.
26
+ */
27
+ export const API_ABSENT_TOKEN = 'api:v1:absent';
28
+ /**
29
+ * Build a VersionToken from a host-supplied ETag or version-header value.
30
+ * null / undefined / empty / whitespace-only → API_ABSENT_TOKEN.
31
+ * Surrounding weak/strong quote marks are stripped once for stable equality.
32
+ */
33
+ export function createApiVersionToken(etag) {
34
+ if (etag == null)
35
+ return API_ABSENT_TOKEN;
36
+ let s = String(etag).trim();
37
+ if (s.length === 0)
38
+ return API_ABSENT_TOKEN;
39
+ // Strip one layer of optional W/ and quotes: W/"abc" → abc, "abc" → abc
40
+ if (s.startsWith('W/') || s.startsWith('w/'))
41
+ s = s.slice(2).trim();
42
+ if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
43
+ s = s.slice(1, -1);
44
+ }
45
+ if (s.length === 0)
46
+ return API_ABSENT_TOKEN;
47
+ return `${API_VERSION_TOKEN_PREFIX}${s}`;
48
+ }
49
+ /** Raw value embedded in an api:v1: token (for host If-Match headers). Absent → null. */
50
+ export function apiTokenRaw(token) {
51
+ if (typeof token !== 'string' || !token.startsWith(API_VERSION_TOKEN_PREFIX))
52
+ return null;
53
+ if (token === API_ABSENT_TOKEN)
54
+ return null;
55
+ const raw = token.slice(API_VERSION_TOKEN_PREFIX.length);
56
+ return raw.length > 0 ? raw : null;
57
+ }
58
+ /**
59
+ * Conditional API write: re-read ETag; if token matches expected → host write with If-Match context.
60
+ *
61
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
62
+ * committed (+ optional new_etag) → committed (version_token = expected; result carries new_etag)
63
+ * precondition_failed (412 class) → refused / stale_version_token
64
+ * current_token from current_etag when host supplied it,
65
+ * else null (no invented token)
66
+ *
67
+ * Missing new_etag on success: stored as null on result; detect_stale_during_commit is a no-op
68
+ * unless new_etag is a non-empty string (honest — no ABSENT fabrication as "expected after").
69
+ *
70
+ * Wired through executeIfUnchanged — not a parallel path.
71
+ */
72
+ export async function writeApiIfUnchanged(args) {
73
+ const detect = args.detect_stale_during_commit === true;
74
+ return executeIfUnchanged({
75
+ expected_token: args.expected_token,
76
+ current_token: async () => createApiVersionToken(await args.current_etag()),
77
+ detect_stale_during_commit: detect,
78
+ expected_after_commit: detect
79
+ ? async (written) => {
80
+ if (typeof written.new_etag === 'string' && written.new_etag.trim().length > 0) {
81
+ return createApiVersionToken(written.new_etag);
82
+ }
83
+ // No intended post-ETag: return live re-read so tokensEqual is tautological (detect no-op).
84
+ return createApiVersionToken(await args.current_etag());
85
+ }
86
+ : undefined,
87
+ write: async () => {
88
+ const report = await args.write({
89
+ if_match: apiTokenRaw(args.expected_token),
90
+ expected_token: args.expected_token,
91
+ });
92
+ if (!report || typeof report !== 'object') {
93
+ throw new Error('writeApiIfUnchanged: host write must return ApiHostWriteReport');
94
+ }
95
+ if (report.status === 'precondition_failed') {
96
+ const cur = report.current_etag !== undefined
97
+ ? createApiVersionToken(report.current_etag)
98
+ : null;
99
+ throw new StaleVersionTokenAbort(cur);
100
+ }
101
+ if (report.status !== 'committed') {
102
+ throw new Error(`writeApiIfUnchanged: unknown host report status ${String(report.status)}`);
103
+ }
104
+ const new_etag = report.new_etag === undefined || report.new_etag === null
105
+ ? null
106
+ : String(report.new_etag);
107
+ return {
108
+ new_etag,
109
+ result: report.result,
110
+ };
111
+ },
112
+ });
113
+ }
114
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
115
+ export { tokensEqual };
116
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/cas-adapters/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Database optimistic-lock CAS adapter — version-column / rowversion discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * db:v1:<version_column_value>
9
+ * Missing / empty version → DB_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter never composes SQL and never opens a connection. The host callback
12
+ * performs the UPDATE … WHERE version = $expected (or equivalent) and reports rows_affected
13
+ * or an explicit committed/conflict shape. Same honesty class as host-asserted conditional write.
14
+ *
15
+ * No pg/mysql/sqlite drivers — all I/O is host-injected.
16
+ */
17
+ import { tokensEqual, type ExecuteIfUnchangedOutcome, type VersionToken } from '../conditional-write.js';
18
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
19
+ export declare const DB_VERSION_TOKEN_PREFIX = "db:v1:";
20
+ /**
21
+ * Token used when the row has no version yet (insert path) or host reported null.
22
+ * Hosts that measured absence should pass this as expected_token for first write.
23
+ */
24
+ export declare const DB_ABSENT_TOKEN: VersionToken;
25
+ /**
26
+ * Build a VersionToken from a host-read version column / rowversion / xmin-style value.
27
+ * null / undefined / empty → DB_ABSENT_TOKEN. Numbers are stringified (no format invention).
28
+ */
29
+ export declare function createDbVersionToken(version: string | number | bigint | null | undefined): VersionToken;
30
+ /** Raw version embedded in a db:v1: token (for host SQL bind params). Absent → null. */
31
+ export declare function dbTokenRaw(token: VersionToken | null | undefined): string | null;
32
+ /**
33
+ * Host report after attempting an optimistic-lock write.
34
+ * Prefer rows_affected (UPDATE … WHERE version = $expected) or explicit committed/conflict.
35
+ */
36
+ export type DbHostWriteReport<T = unknown> = {
37
+ status: 'committed';
38
+ /** Version after write when the host can report it; null/omit when unknown. */
39
+ new_version?: string | number | bigint | null;
40
+ result?: T;
41
+ } | {
42
+ /** Optimistic lock lost — zero rows updated. */
43
+ status: 'conflict';
44
+ /** Current version if the host re-read it; omit → null (no invented token). */
45
+ current_version?: string | number | bigint | null;
46
+ } | {
47
+ /**
48
+ * Alternate committed shape: rows_affected > 0 means committed.
49
+ * rows_affected === 0 means conflict (mapped to refused).
50
+ */
51
+ rows_affected: number;
52
+ new_version?: string | number | bigint | null;
53
+ result?: T;
54
+ };
55
+ export type WriteDbIfUnchangedArgs<T = unknown> = {
56
+ /** Token measured before the host decided to write (from createDbVersionToken). */
57
+ expected_token: VersionToken;
58
+ /** Re-read the row version column. Called pre-write and post-write when detect is on. */
59
+ current_version: () => string | number | bigint | null | Promise<string | number | bigint | null>;
60
+ /**
61
+ * Host performs the optimistic-lock mutation (SQL/query builder — never composed here).
62
+ * Receives the raw expected version for the WHERE clause bind.
63
+ */
64
+ write: (ctx: {
65
+ expected_version: string | null;
66
+ expected_token: VersionToken;
67
+ }) => DbHostWriteReport<T> | Promise<DbHostWriteReport<T>>;
68
+ /**
69
+ * Opt-in post-commit detection (default false).
70
+ * When host omits new_version on success, detection is a no-op (cannot know intended post state).
71
+ */
72
+ detect_stale_during_commit?: boolean;
73
+ };
74
+ export type DbWriteResult<T = unknown> = {
75
+ new_version: string | null;
76
+ result: T | undefined;
77
+ rows_affected?: number;
78
+ };
79
+ /**
80
+ * Conditional DB write: re-read version; if token matches → host optimistic-lock write.
81
+ *
82
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
83
+ * committed / rows_affected > 0 → committed
84
+ * conflict / rows_affected === 0 → refused / stale_version_token
85
+ * current_token from current_version when supplied,
86
+ * else null (no invented token)
87
+ *
88
+ * Wired through executeIfUnchanged — not a parallel path. Never composes SQL.
89
+ */
90
+ export declare function writeDbIfUnchanged<T = unknown>(args: WriteDbIfUnchangedArgs<T>): Promise<ExecuteIfUnchangedOutcome<DbWriteResult<T>>>;
91
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
92
+ export { tokensEqual };
93
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AAEjC,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAE5D;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GACnD,YAAY,CAMd;AAED,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAKhF;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IACrC;IACE,MAAM,EAAE,WAAW,CAAC;IACpB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ,GACD;IACE,gDAAgD;IAChD,MAAM,EAAE,UAAU,CAAC;IACnB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACnD,GACD;IACE;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,EAAE,CAAC,CAAC;CACZ,CAAC;AAEN,MAAM,MAAM,sBAAsB,CAAC,CAAC,GAAG,OAAO,IAAI;IAChD,mFAAmF;IACnF,cAAc,EAAE,YAAY,CAAC;IAC7B,yFAAyF;IACzF,eAAe,EAAE,MACb,MAAM,GACN,MAAM,GACN,MAAM,GACN,IAAI,GACJ,OAAO,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C;;;OAGG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE;QACX,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,cAAc,EAAE,YAAY,CAAC;KAC9B,KAAK,iBAAiB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D;;;OAGG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI;IACvC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAwDF;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAClD,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAC9B,OAAO,CAAC,yBAAyB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAuCtD;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Database optimistic-lock CAS adapter — version-column / rowversion discipline over host-injected I/O.
3
+ *
4
+ * Uses executeIfUnchanged (conditional-write.ts). Does not invent a parallel outcome shape.
5
+ * Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * db:v1:<version_column_value>
9
+ * Missing / empty version → DB_ABSENT_TOKEN.
10
+ *
11
+ * HONESTY: this adapter never composes SQL and never opens a connection. The host callback
12
+ * performs the UPDATE … WHERE version = $expected (or equivalent) and reports rows_affected
13
+ * or an explicit committed/conflict shape. Same honesty class as host-asserted conditional write.
14
+ *
15
+ * No pg/mysql/sqlite drivers — all I/O is host-injected.
16
+ */
17
+ 'use strict';
18
+ import { executeIfUnchanged, StaleVersionTokenAbort, tokensEqual, } from '../conditional-write.js';
19
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
20
+ export const DB_VERSION_TOKEN_PREFIX = 'db:v1:';
21
+ /**
22
+ * Token used when the row has no version yet (insert path) or host reported null.
23
+ * Hosts that measured absence should pass this as expected_token for first write.
24
+ */
25
+ export const DB_ABSENT_TOKEN = 'db:v1:absent';
26
+ /**
27
+ * Build a VersionToken from a host-read version column / rowversion / xmin-style value.
28
+ * null / undefined / empty → DB_ABSENT_TOKEN. Numbers are stringified (no format invention).
29
+ */
30
+ export function createDbVersionToken(version) {
31
+ if (version == null)
32
+ return DB_ABSENT_TOKEN;
33
+ if (typeof version === 'number' && !Number.isFinite(version))
34
+ return DB_ABSENT_TOKEN;
35
+ const s = String(version).trim();
36
+ if (s.length === 0)
37
+ return DB_ABSENT_TOKEN;
38
+ return `${DB_VERSION_TOKEN_PREFIX}${s}`;
39
+ }
40
+ /** Raw version embedded in a db:v1: token (for host SQL bind params). Absent → null. */
41
+ export function dbTokenRaw(token) {
42
+ if (typeof token !== 'string' || !token.startsWith(DB_VERSION_TOKEN_PREFIX))
43
+ return null;
44
+ if (token === DB_ABSENT_TOKEN)
45
+ return null;
46
+ const raw = token.slice(DB_VERSION_TOKEN_PREFIX.length);
47
+ return raw.length > 0 ? raw : null;
48
+ }
49
+ function normalizeDbReport(report) {
50
+ if ('rows_affected' in report && typeof report.rows_affected === 'number') {
51
+ const r = report;
52
+ if (r.rows_affected === 0) {
53
+ return {
54
+ kind: 'conflict',
55
+ new_version: undefined,
56
+ current_version: undefined,
57
+ rows_affected: 0,
58
+ };
59
+ }
60
+ return {
61
+ kind: 'committed',
62
+ new_version: r.new_version,
63
+ result: r.result,
64
+ rows_affected: r.rows_affected,
65
+ };
66
+ }
67
+ if ('status' in report && report.status === 'conflict') {
68
+ const r = report;
69
+ return {
70
+ kind: 'conflict',
71
+ new_version: undefined,
72
+ current_version: r.current_version,
73
+ };
74
+ }
75
+ if ('status' in report && report.status === 'committed') {
76
+ const r = report;
77
+ return {
78
+ kind: 'committed',
79
+ new_version: r.new_version,
80
+ result: r.result,
81
+ };
82
+ }
83
+ throw new Error('writeDbIfUnchanged: host write must return DbHostWriteReport');
84
+ }
85
+ /**
86
+ * Conditional DB write: re-read version; if token matches → host optimistic-lock write.
87
+ *
88
+ * Mapping (host report → ExecuteIfUnchangedOutcome):
89
+ * committed / rows_affected > 0 → committed
90
+ * conflict / rows_affected === 0 → refused / stale_version_token
91
+ * current_token from current_version when supplied,
92
+ * else null (no invented token)
93
+ *
94
+ * Wired through executeIfUnchanged — not a parallel path. Never composes SQL.
95
+ */
96
+ export async function writeDbIfUnchanged(args) {
97
+ const detect = args.detect_stale_during_commit === true;
98
+ return executeIfUnchanged({
99
+ expected_token: args.expected_token,
100
+ current_token: async () => createDbVersionToken(await args.current_version()),
101
+ detect_stale_during_commit: detect,
102
+ expected_after_commit: detect
103
+ ? async (written) => {
104
+ if (typeof written.new_version === 'string' && written.new_version.trim().length > 0) {
105
+ return createDbVersionToken(written.new_version);
106
+ }
107
+ return createDbVersionToken(await args.current_version());
108
+ }
109
+ : undefined,
110
+ write: async () => {
111
+ const report = await args.write({
112
+ expected_version: dbTokenRaw(args.expected_token),
113
+ expected_token: args.expected_token,
114
+ });
115
+ const norm = normalizeDbReport(report);
116
+ if (norm.kind === 'conflict') {
117
+ const cur = norm.current_version !== undefined
118
+ ? createDbVersionToken(norm.current_version)
119
+ : null;
120
+ throw new StaleVersionTokenAbort(cur);
121
+ }
122
+ const new_version = norm.new_version === undefined || norm.new_version === null
123
+ ? null
124
+ : String(norm.new_version);
125
+ return {
126
+ new_version,
127
+ result: norm.result,
128
+ rows_affected: norm.rows_affected,
129
+ };
130
+ },
131
+ });
132
+ }
133
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
134
+ export { tokensEqual };
135
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/cas-adapters/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Filesystem CAS adapter — mtime + content-hash VersionToken and atomic-rename write.
3
+ *
4
+ * Uses the host contract helper executeIfUnchanged (conditional-write.ts). Does not invent
5
+ * a parallel outcome shape. Token format is opaque to the guard (string equality only).
6
+ *
7
+ * Token encoding (this adapter only; not interpreted by the core):
8
+ * fs:v1:<mtime_ms>:<sha256_hex_of_file_bytes>
9
+ * Missing file → createFsVersionToken still works for "absent" via a dedicated absent token
10
+ * only if we choose; for create we require the file to exist (ENOENT throws) so hosts
11
+ * measure an existing prior. For write of new files, expected_token may be ABSENT_TOKEN.
12
+ */
13
+ import { tokensEqual, type ExecuteIfUnchangedOutcome, type VersionToken, type VersionedContent } from '../conditional-write.js';
14
+ import type { PriorContentResolver } from '../freshness.js';
15
+ /** Prefix for this adapter's opaque tokens (equality-only outside this module). */
16
+ export declare const FS_VERSION_TOKEN_PREFIX = "fs:v1:";
17
+ /**
18
+ * Token used when the path does not exist yet (create-if-absent CAS).
19
+ * Hosts that measured absence should pass this as expected_token for first write.
20
+ */
21
+ export declare const FS_ABSENT_TOKEN: VersionToken;
22
+ /** Extract content-hash segment from an fs:v1 token (null if absent/malformed). */
23
+ export declare function fsTokenContentHash(token: VersionToken | null | undefined): string | null;
24
+ /**
25
+ * Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
26
+ * Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
27
+ * so hosts can CAS create vs create-raced.
28
+ */
29
+ export declare function createFsVersionToken(filePath: string): Promise<VersionToken>;
30
+ /**
31
+ * Read file content + version token for VersionedContent (host reporting / preflight prior).
32
+ * ENOENT → content '' and FS_ABSENT_TOKEN.
33
+ */
34
+ export declare function readVersionedFile(filePath: string): Promise<VersionedContent>;
35
+ export type WriteFileIfUnchangedArgs = {
36
+ path: string;
37
+ /** Token measured before the host decided to write (from createFsVersionToken). */
38
+ expected_token: VersionToken;
39
+ /** Full new file contents (utf8 string or Buffer). */
40
+ content: string | Buffer;
41
+ };
42
+ /**
43
+ * Conditional write: re-stat+re-hash; if token matches expected → temp file + atomic rename.
44
+ *
45
+ * MEASURED windows (pre-fix, 2fcd74e-era):
46
+ * 1. executeIfUnchanged: current_token() then await write() — no re-check in between.
47
+ * 2. write(): writeFile(tmp) then rename(tmp,target) with NO re-stat of target before rename
48
+ * (fs.ts former lines 106–108) — concurrent mutation of target is clobbered by rename.
49
+ *
50
+ * Closures:
51
+ * - Pre-rename: re-createFsVersionToken(target); on mismatch abort rename, unlink tmp,
52
+ * throw StaleVersionTokenAbort → refused/stale_version_token (write did not land).
53
+ * - Post-rename: detect_stale_during_commit — content-hash of path must match sha256(body);
54
+ * mismatch → committed_stale_detected (write landed; someone overwrote after rename).
55
+ *
56
+ * Wired through executeIfUnchanged — not a parallel path.
57
+ */
58
+ export declare function writeFileIfUnchanged(args: WriteFileIfUnchangedArgs): Promise<ExecuteIfUnchangedOutcome<{
59
+ path: string;
60
+ bytes: number;
61
+ written_content_hash: string;
62
+ }>>;
63
+ /**
64
+ * Opt-in PriorContentResolver that reads a file path from tool arguments.
65
+ * Returns file utf8 content for freshness measurement (string only — tokens travel separately
66
+ * via versioned_content / conditioned_on_token on the call context).
67
+ *
68
+ * Plug-in: pass as GuardConfig.resolvePriorContent / withCodeRifts({ resolvePriorContent }).
69
+ */
70
+ export declare function createFsPriorContentResolver(options?: {
71
+ /**
72
+ * Map artifactId → absolute or relative file path.
73
+ * If omitted, uses req.path or arguments-style path when provided on the resolve request.
74
+ */
75
+ pathForArtifact?: (artifactId: string) => string | null | undefined;
76
+ }): PriorContentResolver;
77
+ /** Re-export equality helper for hosts comparing tokens without importing core module. */
78
+ export { tokensEqual };
79
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAGL,WAAW,EACX,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,YAA6B,CAAC;AAM5D,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQxF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBlF;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAOnF;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,cAAc,EAAE,YAAY,CAAC;IAC7B,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAsDnG;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,CAAC,EAAE;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACrE,GAAG,oBAAoB,CAoBvB;AAED,0FAA0F;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC"}