@coderifts/agent-guard 6.0.0 → 6.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.
- package/README.md +105 -4
- package/dist/cjs/adapters/anthropic.d.ts +26 -0
- package/dist/cjs/adapters/anthropic.d.ts.map +1 -1
- package/dist/cjs/adapters/anthropic.js +68 -0
- package/dist/cjs/adapters/anthropic.js.map +1 -1
- package/dist/cjs/adapters/gemini.d.ts +39 -0
- package/dist/cjs/adapters/gemini.d.ts.map +1 -1
- package/dist/cjs/adapters/gemini.js +83 -0
- package/dist/cjs/adapters/gemini.js.map +1 -1
- package/dist/cjs/adapters/langgraph.d.ts +29 -0
- package/dist/cjs/adapters/langgraph.d.ts.map +1 -1
- package/dist/cjs/adapters/langgraph.js +70 -0
- package/dist/cjs/adapters/langgraph.js.map +1 -1
- package/dist/cjs/adapters/openai.d.ts +44 -0
- package/dist/cjs/adapters/openai.d.ts.map +1 -1
- package/dist/cjs/adapters/openai.js +86 -0
- package/dist/cjs/adapters/openai.js.map +1 -1
- package/dist/cjs/cas-adapters/fs.d.ts +79 -0
- package/dist/cjs/cas-adapters/fs.d.ts.map +1 -0
- package/dist/cjs/cas-adapters/fs.js +224 -0
- package/dist/cjs/cas-adapters/fs.js.map +1 -0
- package/dist/cjs/conditional-write.d.ts +66 -0
- package/dist/cjs/conditional-write.d.ts.map +1 -1
- package/dist/cjs/conditional-write.js +72 -1
- package/dist/cjs/conditional-write.js.map +1 -1
- package/dist/cjs/execution-time-fingerprint.d.ts +65 -0
- package/dist/cjs/execution-time-fingerprint.d.ts.map +1 -0
- package/dist/cjs/execution-time-fingerprint.js +129 -0
- package/dist/cjs/execution-time-fingerprint.js.map +1 -0
- package/dist/cjs/guard.d.ts.map +1 -1
- package/dist/cjs/guard.js +35 -0
- package/dist/cjs/guard.js.map +1 -1
- package/dist/cjs/index.d.ts +13 -10
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +33 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types.d.ts +23 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/adapters/anthropic.d.ts +26 -0
- package/dist/esm/adapters/anthropic.d.ts.map +1 -1
- package/dist/esm/adapters/anthropic.js +66 -0
- package/dist/esm/adapters/anthropic.js.map +1 -1
- package/dist/esm/adapters/gemini.d.ts +39 -0
- package/dist/esm/adapters/gemini.d.ts.map +1 -1
- package/dist/esm/adapters/gemini.js +81 -0
- package/dist/esm/adapters/gemini.js.map +1 -1
- package/dist/esm/adapters/langgraph.d.ts +29 -0
- package/dist/esm/adapters/langgraph.d.ts.map +1 -1
- package/dist/esm/adapters/langgraph.js +68 -0
- package/dist/esm/adapters/langgraph.js.map +1 -1
- package/dist/esm/adapters/openai.d.ts +44 -0
- package/dist/esm/adapters/openai.d.ts.map +1 -1
- package/dist/esm/adapters/openai.js +84 -0
- package/dist/esm/adapters/openai.js.map +1 -1
- package/dist/esm/cas-adapters/fs.d.ts +79 -0
- package/dist/esm/cas-adapters/fs.d.ts.map +1 -0
- package/dist/esm/cas-adapters/fs.js +185 -0
- package/dist/esm/cas-adapters/fs.js.map +1 -0
- package/dist/esm/conditional-write.d.ts +66 -0
- package/dist/esm/conditional-write.d.ts.map +1 -1
- package/dist/esm/conditional-write.js +69 -0
- package/dist/esm/conditional-write.js.map +1 -1
- package/dist/esm/execution-time-fingerprint.d.ts +65 -0
- package/dist/esm/execution-time-fingerprint.d.ts.map +1 -0
- package/dist/esm/execution-time-fingerprint.js +123 -0
- package/dist/esm/execution-time-fingerprint.js.map +1 -0
- package/dist/esm/guard.d.ts.map +1 -1
- package/dist/esm/guard.js +35 -0
- package/dist/esm/guard.js.map +1 -1
- package/dist/esm/index.d.ts +13 -10
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +15 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +23 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* Mirrors src/adapters/openai.ts and anthropic.ts — only the target tool shape differs.
|
|
34
34
|
*/
|
|
35
35
|
import { withCodeRifts } from '../with-coderifts.js';
|
|
36
|
+
import { attachProofToAgentResponse } from '../final-answer-proof.js';
|
|
36
37
|
/** Empty JSON Schema object — used when a ProtectedTool has no inputSchema. */
|
|
37
38
|
const EMPTY_SCHEMA = Object.freeze({
|
|
38
39
|
type: 'object',
|
|
@@ -104,4 +105,71 @@ export function langGraphToolAdapter(result) {
|
|
|
104
105
|
}
|
|
105
106
|
return out;
|
|
106
107
|
}
|
|
108
|
+
export function defaultSerializeLangGraphToolResult(result) {
|
|
109
|
+
if (typeof result === 'string')
|
|
110
|
+
return result;
|
|
111
|
+
if (typeof result === 'number'
|
|
112
|
+
|| typeof result === 'boolean'
|
|
113
|
+
|| typeof result === 'bigint'
|
|
114
|
+
|| result === null
|
|
115
|
+
|| result === undefined) {
|
|
116
|
+
return String(result);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return JSON.stringify(result);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return String(result);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Map a full GuardOutcome into a LangGraph/LangChain ToolMessage shape with proof embedded.
|
|
127
|
+
* Same arm mapping as bindOpenAIGuardOutcome; id field is tool_call_id.
|
|
128
|
+
*/
|
|
129
|
+
export function bindLangGraphGuardOutcome(outcome, args) {
|
|
130
|
+
const tool_call_id = args.tool_call_id;
|
|
131
|
+
const serialize = args.serialize ?? defaultSerializeLangGraphToolResult;
|
|
132
|
+
let body;
|
|
133
|
+
if (outcome.executed === true) {
|
|
134
|
+
body = serialize(outcome.result);
|
|
135
|
+
}
|
|
136
|
+
else if (outcome.executionAttempted === false) {
|
|
137
|
+
const kind = verdictKind(outcome);
|
|
138
|
+
body =
|
|
139
|
+
`CodeRifts gate did not permit execution (verdict: ${kind}). `
|
|
140
|
+
+ 'No tool result was produced.';
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const err = 'error' in outcome ? outcome.error : undefined;
|
|
144
|
+
const kind = verdictKind(outcome);
|
|
145
|
+
body =
|
|
146
|
+
`Tool execution failed after gate decision (verdict: ${kind}): ${formatGuardError(err)}`;
|
|
147
|
+
}
|
|
148
|
+
const content = attachProofToAgentResponse(body, outcome.proof);
|
|
149
|
+
const msg = {
|
|
150
|
+
content,
|
|
151
|
+
tool_call_id,
|
|
152
|
+
};
|
|
153
|
+
if (args.name != null && args.name !== '') {
|
|
154
|
+
msg.name = args.name;
|
|
155
|
+
}
|
|
156
|
+
return msg;
|
|
157
|
+
}
|
|
158
|
+
function verdictKind(outcome) {
|
|
159
|
+
return outcome.verdict && typeof outcome.verdict === 'object' && 'kind' in outcome.verdict
|
|
160
|
+
? String(outcome.verdict.kind)
|
|
161
|
+
: 'UNKNOWN';
|
|
162
|
+
}
|
|
163
|
+
function formatGuardError(err) {
|
|
164
|
+
if (err instanceof Error)
|
|
165
|
+
return err.message || err.name || 'Error';
|
|
166
|
+
if (typeof err === 'string')
|
|
167
|
+
return err;
|
|
168
|
+
try {
|
|
169
|
+
return JSON.stringify(err);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return String(err);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
107
175
|
//# sourceMappingURL=langgraph.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langgraph.js","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"langgraph.js","sourceRoot":"","sources":["../../../src/adapters/langgraph.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { WithCodeRiftsInput, WithCodeRiftsResult, CompositionAssurance, ReceiptThreadHandle } from '../with-coderifts.js';
|
|
25
25
|
import type { ProtectedTool, RegistryCoverageReport } from '../tool-registry.js';
|
|
26
|
+
import type { GuardOutcome } from '../types.js';
|
|
26
27
|
/**
|
|
27
28
|
* OpenAI chat.completions `tools[]` element (function tool).
|
|
28
29
|
* @see https://platform.openai.com/docs/guides/function-calling
|
|
@@ -90,4 +91,47 @@ export declare function withCodeRiftsOpenAI(input: WithCodeRiftsInput): WithCode
|
|
|
90
91
|
* Does not re-run the guard; does not invent assurance.
|
|
91
92
|
*/
|
|
92
93
|
export declare function openAIToolAdapter(result: WithCodeRiftsResult): WithCodeRiftsOpenAIResult;
|
|
94
|
+
/**
|
|
95
|
+
* Minimal OpenAI chat tool-message (role tool). No openai SDK dependency.
|
|
96
|
+
* @see https://platform.openai.com/docs/guides/function-calling
|
|
97
|
+
*/
|
|
98
|
+
export type OpenAIToolMessage = {
|
|
99
|
+
role: 'tool';
|
|
100
|
+
tool_call_id: string;
|
|
101
|
+
content: string;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Brand for proof-bound OpenAI tool messages (compile-time detection of proof-forgotten paths).
|
|
105
|
+
* Type-level only — same pattern as ReceiptVerifiedEnvelope; not a runtime wire field.
|
|
106
|
+
*/
|
|
107
|
+
declare const __proofBoundBrand: unique symbol;
|
|
108
|
+
export type ProofBoundOpenAIToolMessage = OpenAIToolMessage & {
|
|
109
|
+
readonly __proofBound: typeof __proofBoundBrand;
|
|
110
|
+
};
|
|
111
|
+
export type BindOpenAIGuardOutcomeArgs<T> = {
|
|
112
|
+
/** OpenAI tool_call_id this message answers. */
|
|
113
|
+
tool_call_id: string;
|
|
114
|
+
/** Override result serialization (default: JSON.stringify objects, String() primitives). */
|
|
115
|
+
serialize?: (result: T) => string;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Default result → content body serializer (pure).
|
|
119
|
+
* Objects/arrays → JSON.stringify; strings unchanged; other primitives → String().
|
|
120
|
+
*/
|
|
121
|
+
export declare function defaultSerializeOpenAIToolResult<T>(result: T): string;
|
|
122
|
+
/**
|
|
123
|
+
* Map a full GuardOutcome into an OpenAI tool message with the execution proof embedded.
|
|
124
|
+
*
|
|
125
|
+
* Option B (proof-binding helper): host still calls guardToolCall; this only binds
|
|
126
|
+
* outcome → framework tool-result. Pure and non-mutating. Always embeds outcome.proof
|
|
127
|
+
* (present on every arm) via attachProofToAgentResponse / renderFinalAnswerProof —
|
|
128
|
+
* does not reinvent proof formatting.
|
|
129
|
+
*
|
|
130
|
+
* Arms:
|
|
131
|
+
* - executed (result): serialized result + rendered proof
|
|
132
|
+
* - blocked before factory: gate-denied text (no fabricated result) + proof
|
|
133
|
+
* - factory threw: error indication + proof
|
|
134
|
+
*/
|
|
135
|
+
export declare function bindOpenAIGuardOutcome<T>(outcome: GuardOutcome<T>, args: BindOpenAIGuardOutcomeArgs<T>): ProofBoundOpenAIToolMessage;
|
|
136
|
+
export {};
|
|
93
137
|
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,2EAA2E;QAC3E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,CAAC;CACH,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,+EAA+E;IAC/E,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,wDAAwD;IACxD,eAAe,EAAE,sBAAsB,CAAC;IACxC;;;;OAIG;IACH,qBAAqB,EAAE,oBAAoB,CAAC;IAC5C,yEAAyE;IACzE,cAAc,EAAE,mBAAmB,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAQF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,GAAG,kBAAkB,CAgB7E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,cAAc,EAAE,SAAS,aAAa,EAAE,GAAG,kBAAkB,EAAE,CAE5F;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,yBAAyB,CAGxF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,yBAAyB,CAcxF;AAID;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,OAAO,CAAC,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;AAC/C,MAAM,MAAM,2BAA2B,GAAG,iBAAiB,GAAG;IAC5D,QAAQ,CAAC,YAAY,EAAE,OAAO,iBAAiB,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAgBrE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EACxB,IAAI,EAAE,0BAA0B,CAAC,CAAC,CAAC,GAClC,2BAA2B,CAqC7B"}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* LangGraph / Anthropic adapters follow the same thin-converter pattern later.
|
|
23
23
|
*/
|
|
24
24
|
import { withCodeRifts } from '../with-coderifts.js';
|
|
25
|
+
import { attachProofToAgentResponse } from '../final-answer-proof.js';
|
|
25
26
|
/** Empty JSON Schema object — used when a ProtectedTool has no inputSchema. */
|
|
26
27
|
const EMPTY_PARAMETERS = Object.freeze({
|
|
27
28
|
type: 'object',
|
|
@@ -88,4 +89,87 @@ export function openAIToolAdapter(result) {
|
|
|
88
89
|
}
|
|
89
90
|
return out;
|
|
90
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Default result → content body serializer (pure).
|
|
94
|
+
* Objects/arrays → JSON.stringify; strings unchanged; other primitives → String().
|
|
95
|
+
*/
|
|
96
|
+
export function defaultSerializeOpenAIToolResult(result) {
|
|
97
|
+
if (typeof result === 'string')
|
|
98
|
+
return result;
|
|
99
|
+
if (typeof result === 'number'
|
|
100
|
+
|| typeof result === 'boolean'
|
|
101
|
+
|| typeof result === 'bigint'
|
|
102
|
+
|| result === null
|
|
103
|
+
|| result === undefined) {
|
|
104
|
+
return String(result);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return JSON.stringify(result);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return String(result);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Map a full GuardOutcome into an OpenAI tool message with the execution proof embedded.
|
|
115
|
+
*
|
|
116
|
+
* Option B (proof-binding helper): host still calls guardToolCall; this only binds
|
|
117
|
+
* outcome → framework tool-result. Pure and non-mutating. Always embeds outcome.proof
|
|
118
|
+
* (present on every arm) via attachProofToAgentResponse / renderFinalAnswerProof —
|
|
119
|
+
* does not reinvent proof formatting.
|
|
120
|
+
*
|
|
121
|
+
* Arms:
|
|
122
|
+
* - executed (result): serialized result + rendered proof
|
|
123
|
+
* - blocked before factory: gate-denied text (no fabricated result) + proof
|
|
124
|
+
* - factory threw: error indication + proof
|
|
125
|
+
*/
|
|
126
|
+
export function bindOpenAIGuardOutcome(outcome, args) {
|
|
127
|
+
const tool_call_id = args.tool_call_id;
|
|
128
|
+
const serialize = args.serialize ?? defaultSerializeOpenAIToolResult;
|
|
129
|
+
let body;
|
|
130
|
+
if (outcome.executed === true) {
|
|
131
|
+
// Executed arms (enforced or not): result is present.
|
|
132
|
+
body = serialize(outcome.result);
|
|
133
|
+
}
|
|
134
|
+
else if (outcome.executionAttempted === false) {
|
|
135
|
+
// Blocked before factory — no result; never fabricate one.
|
|
136
|
+
const kind = outcome.verdict && typeof outcome.verdict === 'object' && 'kind' in outcome.verdict
|
|
137
|
+
? String(outcome.verdict.kind)
|
|
138
|
+
: 'UNKNOWN';
|
|
139
|
+
body =
|
|
140
|
+
`CodeRifts gate did not permit execution (verdict: ${kind}). `
|
|
141
|
+
+ 'No tool result was produced.';
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// executionAttempted && !executed → factory threw; error is present.
|
|
145
|
+
const err = 'error' in outcome ? outcome.error : undefined;
|
|
146
|
+
const errText = formatGuardError(err);
|
|
147
|
+
const kind = outcome.verdict && typeof outcome.verdict === 'object' && 'kind' in outcome.verdict
|
|
148
|
+
? String(outcome.verdict.kind)
|
|
149
|
+
: 'UNKNOWN';
|
|
150
|
+
body =
|
|
151
|
+
`Tool execution failed after gate decision (verdict: ${kind}): ${errText}`;
|
|
152
|
+
}
|
|
153
|
+
// Reuse public proof render path (string append) — same separator/render as final-answer attach.
|
|
154
|
+
const content = attachProofToAgentResponse(body, outcome.proof);
|
|
155
|
+
// Type brand only (no extra wire keys — OpenAI tool messages stay {role, tool_call_id, content}).
|
|
156
|
+
const msg = {
|
|
157
|
+
role: 'tool',
|
|
158
|
+
tool_call_id,
|
|
159
|
+
content,
|
|
160
|
+
};
|
|
161
|
+
return msg;
|
|
162
|
+
}
|
|
163
|
+
function formatGuardError(err) {
|
|
164
|
+
if (err instanceof Error)
|
|
165
|
+
return err.message || err.name || 'Error';
|
|
166
|
+
if (typeof err === 'string')
|
|
167
|
+
return err;
|
|
168
|
+
try {
|
|
169
|
+
return JSON.stringify(err);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return String(err);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
91
175
|
//# sourceMappingURL=openai.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/adapters/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/adapters/openai.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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
'use strict';
|
|
14
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
15
|
+
import { promises as fsp } from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { executeIfUnchanged, StaleVersionTokenAbort, tokensEqual, } from '../conditional-write.js';
|
|
18
|
+
/** Prefix for this adapter's opaque tokens (equality-only outside this module). */
|
|
19
|
+
export const FS_VERSION_TOKEN_PREFIX = 'fs:v1:';
|
|
20
|
+
/**
|
|
21
|
+
* Token used when the path does not exist yet (create-if-absent CAS).
|
|
22
|
+
* Hosts that measured absence should pass this as expected_token for first write.
|
|
23
|
+
*/
|
|
24
|
+
export const FS_ABSENT_TOKEN = 'fs:v1:absent';
|
|
25
|
+
function sha256hex(buf) {
|
|
26
|
+
return createHash('sha256').update(buf).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
/** Extract content-hash segment from an fs:v1 token (null if absent/malformed). */
|
|
29
|
+
export function fsTokenContentHash(token) {
|
|
30
|
+
if (typeof token !== 'string' || !token.startsWith(FS_VERSION_TOKEN_PREFIX))
|
|
31
|
+
return null;
|
|
32
|
+
if (token === FS_ABSENT_TOKEN)
|
|
33
|
+
return null;
|
|
34
|
+
const rest = token.slice(FS_VERSION_TOKEN_PREFIX.length);
|
|
35
|
+
const colon = rest.indexOf(':');
|
|
36
|
+
if (colon < 0)
|
|
37
|
+
return null;
|
|
38
|
+
const hash = rest.slice(colon + 1);
|
|
39
|
+
return /^[a-f0-9]{64}$/.test(hash) ? hash : null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a VersionToken from path: mtime (ms) + sha256 of file bytes.
|
|
43
|
+
* Throws on I/O errors other than callers may catch. ENOENT → returns FS_ABSENT_TOKEN
|
|
44
|
+
* so hosts can CAS create vs create-raced.
|
|
45
|
+
*/
|
|
46
|
+
export async function createFsVersionToken(filePath) {
|
|
47
|
+
let st;
|
|
48
|
+
try {
|
|
49
|
+
st = await fsp.stat(filePath);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const code = err && typeof err === 'object' && 'code' in err
|
|
53
|
+
? String(err.code)
|
|
54
|
+
: '';
|
|
55
|
+
if (code === 'ENOENT')
|
|
56
|
+
return FS_ABSENT_TOKEN;
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
if (!st.isFile()) {
|
|
60
|
+
// Directories / specials are not content CAS targets for this adapter.
|
|
61
|
+
throw new Error(`createFsVersionToken: not a regular file: ${filePath}`);
|
|
62
|
+
}
|
|
63
|
+
const buf = await fsp.readFile(filePath);
|
|
64
|
+
const hash = sha256hex(buf);
|
|
65
|
+
const mtimeMs = Math.trunc(st.mtimeMs);
|
|
66
|
+
return `${FS_VERSION_TOKEN_PREFIX}${mtimeMs}:${hash}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Read file content + version token for VersionedContent (host reporting / preflight prior).
|
|
70
|
+
* ENOENT → content '' and FS_ABSENT_TOKEN.
|
|
71
|
+
*/
|
|
72
|
+
export async function readVersionedFile(filePath) {
|
|
73
|
+
const version_token = await createFsVersionToken(filePath);
|
|
74
|
+
if (version_token === FS_ABSENT_TOKEN) {
|
|
75
|
+
return { content: '', version_token };
|
|
76
|
+
}
|
|
77
|
+
const content = await fsp.readFile(filePath, 'utf8');
|
|
78
|
+
return { content, version_token };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Conditional write: re-stat+re-hash; if token matches expected → temp file + atomic rename.
|
|
82
|
+
*
|
|
83
|
+
* MEASURED windows (pre-fix, 2fcd74e-era):
|
|
84
|
+
* 1. executeIfUnchanged: current_token() then await write() — no re-check in between.
|
|
85
|
+
* 2. write(): writeFile(tmp) then rename(tmp,target) with NO re-stat of target before rename
|
|
86
|
+
* (fs.ts former lines 106–108) — concurrent mutation of target is clobbered by rename.
|
|
87
|
+
*
|
|
88
|
+
* Closures:
|
|
89
|
+
* - Pre-rename: re-createFsVersionToken(target); on mismatch abort rename, unlink tmp,
|
|
90
|
+
* throw StaleVersionTokenAbort → refused/stale_version_token (write did not land).
|
|
91
|
+
* - Post-rename: detect_stale_during_commit — content-hash of path must match sha256(body);
|
|
92
|
+
* mismatch → committed_stale_detected (write landed; someone overwrote after rename).
|
|
93
|
+
*
|
|
94
|
+
* Wired through executeIfUnchanged — not a parallel path.
|
|
95
|
+
*/
|
|
96
|
+
export async function writeFileIfUnchanged(args) {
|
|
97
|
+
const target = path.resolve(args.path);
|
|
98
|
+
const body = typeof args.content === 'string' ? Buffer.from(args.content, 'utf8') : args.content;
|
|
99
|
+
const writtenHash = sha256hex(body);
|
|
100
|
+
return executeIfUnchanged({
|
|
101
|
+
expected_token: args.expected_token,
|
|
102
|
+
current_token: () => createFsVersionToken(target),
|
|
103
|
+
detect_stale_during_commit: true,
|
|
104
|
+
// Post-commit: token content-hash must equal sha256 of what we wrote (mtime may vary).
|
|
105
|
+
expected_after_commit: async (result) => {
|
|
106
|
+
// Synthesize a comparable token: re-read path; equality is content-hash based via tokensEqual
|
|
107
|
+
// only if full token matches — mtime always changes on write, so we return the LIVE post
|
|
108
|
+
// token only when its hash matches writtenHash; else return a sentinel that cannot match.
|
|
109
|
+
const post = await createFsVersionToken(target);
|
|
110
|
+
const postHash = fsTokenContentHash(post);
|
|
111
|
+
if (postHash === result.written_content_hash)
|
|
112
|
+
return post;
|
|
113
|
+
// Force mismatch → committed_stale_detected (detection, not rollback).
|
|
114
|
+
return `${FS_VERSION_TOKEN_PREFIX}0:stale_during_commit_mismatch`;
|
|
115
|
+
},
|
|
116
|
+
write: async () => {
|
|
117
|
+
const dir = path.dirname(target);
|
|
118
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
119
|
+
const tmp = path.join(dir, `.coderifts-cas-${path.basename(target)}-${process.pid}-${randomBytes(6).toString('hex')}.tmp`);
|
|
120
|
+
try {
|
|
121
|
+
await fsp.writeFile(tmp, body);
|
|
122
|
+
// Close pre-rename window: if target moved since the executeIfUnchanged check, do not clobber.
|
|
123
|
+
const still = await createFsVersionToken(target);
|
|
124
|
+
if (!tokensEqual(args.expected_token, still)) {
|
|
125
|
+
try {
|
|
126
|
+
await fsp.unlink(tmp);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
/* ignore */
|
|
130
|
+
}
|
|
131
|
+
throw new StaleVersionTokenAbort(still);
|
|
132
|
+
}
|
|
133
|
+
// Atomic replace on same filesystem (POSIX rename; Windows overwrite where supported).
|
|
134
|
+
await fsp.rename(tmp, target);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (err instanceof StaleVersionTokenAbort)
|
|
138
|
+
throw err;
|
|
139
|
+
// Best-effort cleanup of temp — never leave partial target from failed rename.
|
|
140
|
+
try {
|
|
141
|
+
await fsp.unlink(tmp);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
/* ignore */
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
return { path: target, bytes: body.length, written_content_hash: writtenHash };
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Opt-in PriorContentResolver that reads a file path from tool arguments.
|
|
154
|
+
* Returns file utf8 content for freshness measurement (string only — tokens travel separately
|
|
155
|
+
* via versioned_content / conditioned_on_token on the call context).
|
|
156
|
+
*
|
|
157
|
+
* Plug-in: pass as GuardConfig.resolvePriorContent / withCodeRifts({ resolvePriorContent }).
|
|
158
|
+
*/
|
|
159
|
+
export function createFsPriorContentResolver(options) {
|
|
160
|
+
const pathForArtifact = options?.pathForArtifact;
|
|
161
|
+
return async (req) => {
|
|
162
|
+
let filePath = typeof req.path === 'string' && req.path.length > 0 ? req.path : undefined;
|
|
163
|
+
if (!filePath && pathForArtifact) {
|
|
164
|
+
const mapped = pathForArtifact(req.artifactId);
|
|
165
|
+
if (typeof mapped === 'string' && mapped.length > 0)
|
|
166
|
+
filePath = mapped;
|
|
167
|
+
}
|
|
168
|
+
if (!filePath)
|
|
169
|
+
return null;
|
|
170
|
+
try {
|
|
171
|
+
return await fsp.readFile(filePath, 'utf8');
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
const code = err && typeof err === 'object' && 'code' in err
|
|
175
|
+
? String(err.code)
|
|
176
|
+
: '';
|
|
177
|
+
if (code === 'ENOENT')
|
|
178
|
+
return null;
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** Re-export equality helper for hosts comparing tokens without importing core module. */
|
|
184
|
+
export { tokensEqual };
|
|
185
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../src/cas-adapters/fs.ts"],"names":[],"mappings":"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"}
|
|
@@ -89,4 +89,70 @@ export declare function conditionalWriteResidual(input: {
|
|
|
89
89
|
writeStyle: boolean;
|
|
90
90
|
conditionalWrite: ConditionalWriteReport;
|
|
91
91
|
}): string | null;
|
|
92
|
+
/**
|
|
93
|
+
* Outcome of a conditional write attempt.
|
|
94
|
+
* - committed: write ran under the expected token; post-check (if any) agreed
|
|
95
|
+
* - refused / stale_version_token: resource token moved BEFORE write; write did NOT run
|
|
96
|
+
* - committed_stale_detected / stale_during_commit: write DID run, but a post-commit
|
|
97
|
+
* re-check found the resource no longer matches the write's expected post-state
|
|
98
|
+
* (detection only — not a rollback)
|
|
99
|
+
*/
|
|
100
|
+
export type ExecuteIfUnchangedOutcome<T> = {
|
|
101
|
+
status: 'committed';
|
|
102
|
+
result: T;
|
|
103
|
+
version_token: VersionToken;
|
|
104
|
+
} | {
|
|
105
|
+
status: 'refused';
|
|
106
|
+
reason: 'stale_version_token';
|
|
107
|
+
expected_token: VersionToken;
|
|
108
|
+
current_token: VersionToken | null;
|
|
109
|
+
} | {
|
|
110
|
+
status: 'committed_stale_detected';
|
|
111
|
+
reason: 'stale_during_commit';
|
|
112
|
+
result: T;
|
|
113
|
+
expected_token: VersionToken;
|
|
114
|
+
post_commit_token: VersionToken | null;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Thrown from write() when the adapter aborts BEFORE applying the mutation
|
|
118
|
+
* (e.g. pre-rename re-check). executeIfUnchanged maps this to status:'refused'.
|
|
119
|
+
* Not a public host API — adapters use it to stay on the single helper path.
|
|
120
|
+
*/
|
|
121
|
+
export declare class StaleVersionTokenAbort extends Error {
|
|
122
|
+
readonly current_token: VersionToken | null;
|
|
123
|
+
constructor(current_token: VersionToken | null);
|
|
124
|
+
}
|
|
125
|
+
export type ExecuteIfUnchangedArgs<T> = {
|
|
126
|
+
/** Token the host measured before deciding to write (opaque string). */
|
|
127
|
+
expected_token: VersionToken;
|
|
128
|
+
/**
|
|
129
|
+
* Re-read the resource's token immediately before commit.
|
|
130
|
+
* Return null when the resource is gone / unreadable (treated as mismatch).
|
|
131
|
+
*/
|
|
132
|
+
current_token: () => VersionToken | null | Promise<VersionToken | null>;
|
|
133
|
+
/** Mutation applied ONLY when current_token equals expected_token. */
|
|
134
|
+
write: () => T | Promise<T>;
|
|
135
|
+
/**
|
|
136
|
+
* Opt-in post-commit detection (default false — byte-identical old paths).
|
|
137
|
+
* When true AND expected_after_commit is provided: after write(), re-read
|
|
138
|
+
* current_token and compare to expected_after_commit(result). Mismatch →
|
|
139
|
+
* committed_stale_detected (write already happened; honest report only).
|
|
140
|
+
*
|
|
141
|
+
* NOTE: do NOT compare post-commit to expected_token for content-changing
|
|
142
|
+
* writes — the token is supposed to change. Supply the token of the written state.
|
|
143
|
+
*/
|
|
144
|
+
detect_stale_during_commit?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Token that should hold after a successful write (the written state's token).
|
|
147
|
+
* Required when detect_stale_during_commit is true; ignored otherwise.
|
|
148
|
+
*/
|
|
149
|
+
expected_after_commit?: (result: T) => VersionToken | null | Promise<VersionToken | null>;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Host contract helper: re-check version token, then write or refuse.
|
|
153
|
+
* Equality via tokensEqual only — never interprets token format.
|
|
154
|
+
* Never throws for stale tokens (returns refused / committed_stale_detected);
|
|
155
|
+
* write/current_token errors (other than StaleVersionTokenAbort) propagate.
|
|
156
|
+
*/
|
|
157
|
+
export declare function executeIfUnchanged<T>(args: ExecuteIfUnchangedArgs<T>): Promise<ExecuteIfUnchangedOutcome<T>>;
|
|
92
158
|
//# sourceMappingURL=conditional-write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditional-write.d.ts","sourceRoot":"","sources":["../../src/conditional-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,GAAG,KAAK,GAAG,cAAc,CAAC;AAEnE;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,iBAAiB,EAAE,sBAAsB,CAAC;IAC1C;;;OAGG;IACH,oBAAoB,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3C,6DAA6D;IAC7D,yBAAyB,EAAE,OAAO,CAAC;IACnC,oDAAoD;IACpD,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,oBAAoB,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3C,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC7C,CAAC;AAEF,0EAA0E;AAC1E,wBAAgB,WAAW,CACzB,CAAC,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EAClC,CAAC,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GACjC,OAAO,CAIT;AASD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,UAAU,EAAE,OAAO,CAAC;IACpB,uBAAuB,EAAE,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC1C,GAAG;IACF,KAAK,EAAE,qBAAqB,CAAC;IAC7B,mDAAmD;IACnD,UAAU,CAAC,EAAE,4BAA4B,CAAC;CAC3C,CAgCA;AAED,yFAAyF;AACzF,eAAO,MAAM,4BAA4B,iDAAiD,CAAC;AAE3F;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,uBAAuB,EAAE,OAAO,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,sBAAsB,CAAC;CAC1C,GAAG,MAAM,GAAG,IAAI,CAKhB"}
|
|
1
|
+
{"version":3,"file":"conditional-write.d.ts","sourceRoot":"","sources":["../../src/conditional-write.ts"],"names":[],"mappings":"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"}
|