deepline 0.3.90 → 0.3.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/client.ts +101 -6
- package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
- package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
- package/dist/bundling-sources/sdk/src/release.ts +7 -12
- package/dist/bundling-sources/sdk/src/types.ts +6 -1
- package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
- package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +5 -1
- package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
- package/dist/bundling-sources/shared_libs/monitors/package.json +107 -5
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +12 -7
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +48 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +46 -3
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
- package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
- package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
- package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
- package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
- package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
- package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +79 -6
- package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
- package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
- package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
- package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
- package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
- package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
- package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
- package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
- package/dist/cli/index.js +369 -162
- package/dist/cli/index.mjs +369 -162
- package/dist/{compiler-manifest-BaJfE-5H.d.mts → compiler-manifest-x14KL83E.d.mts} +1 -1
- package/dist/{compiler-manifest-BaJfE-5H.d.ts → compiler-manifest-x14KL83E.d.ts} +1 -1
- package/dist/index.d.mts +45 -8
- package/dist/index.d.ts +45 -8
- package/dist/index.js +61 -14
- package/dist/index.mjs +61 -14
- package/dist/install-integrity.json +54 -2
- package/dist/monitor-fleet-contract.d.mts +3 -10
- package/dist/monitor-fleet-contract.d.ts +3 -10
- package/dist/monitor-fleet-contract.js +2 -0
- package/dist/monitor-fleet-contract.mjs +1 -1
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/release.d.mts +3 -3
- package/dist/release.d.ts +3 -3
- package/dist/release.js +7 -12
- package/dist/release.mjs +7 -12
- package/package.json +2 -2
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factor structure of a code transform — the descriptive shape a trace UI needs
|
|
3
|
+
* to render an authored lambda as readable factors instead of raw code.
|
|
4
|
+
*
|
|
5
|
+
* A cell produced by a provider call explains itself: there is a tool, an
|
|
6
|
+
* input, a response. A cell produced by CODE does not. The trace can show the
|
|
7
|
+
* lambda verbatim, and most readers cannot read it. This module's types
|
|
8
|
+
* describe the one thing that IS mechanically recoverable from the source: the
|
|
9
|
+
* named intermediate steps, what each one reads, and how they combine.
|
|
10
|
+
*
|
|
11
|
+
* Three properties are load-bearing:
|
|
12
|
+
*
|
|
13
|
+
* 1. **Static, never evaluated.** Nothing here says which branch fired. The
|
|
14
|
+
* structure states "40 when employees >= 1000, else 25 when >= 200, else
|
|
15
|
+
* 10"; the UI juxtaposes the row's actual values beside it so a human draws
|
|
16
|
+
* the conclusion. An extractor that evaluated would be a second, silently
|
|
17
|
+
* divergent implementation of the runtime.
|
|
18
|
+
* 2. **Whole or nothing.** A lambda outside the supported shape yields no
|
|
19
|
+
* structure at all, with a reason. A structure that quietly dropped a
|
|
20
|
+
* statement would read as complete and be a lie, which is worse for a
|
|
21
|
+
* provenance surface than showing the code.
|
|
22
|
+
* 3. **Partial only when marked.** Where a sub-expression cannot be structured,
|
|
23
|
+
* it is carried verbatim and flagged (`opaque`), so the renderer shows the
|
|
24
|
+
* author's own text rather than a paraphrase it cannot justify.
|
|
25
|
+
*
|
|
26
|
+
* These types are deliberately dependency-free: the extractor (which needs the
|
|
27
|
+
* TypeScript compiler) and the renderer model (which runs in the browser) both
|
|
28
|
+
* import them, and only the extractor may pay for the compiler.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** A column read off the transform's row parameter. */
|
|
32
|
+
export type TransformInputRef = {
|
|
33
|
+
/** Dataset column, e.g. `company_profile` for `row.company_profile`. */
|
|
34
|
+
column: string;
|
|
35
|
+
/** Property path under the column, e.g. `['basic_info','name']`. */
|
|
36
|
+
path?: string[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Everything an expression reaches for.
|
|
41
|
+
*
|
|
42
|
+
* `externals` are identifiers resolved outside the lambda that are not calls —
|
|
43
|
+
* a module constant, say. They are named rather than inlined: the extractor
|
|
44
|
+
* reads one lambda, and claiming to know an external's value would be a guess.
|
|
45
|
+
*/
|
|
46
|
+
export type TransformRefs = {
|
|
47
|
+
inputs: TransformInputRef[];
|
|
48
|
+
factors: string[];
|
|
49
|
+
/** Called functions, by written name (`employeeFloor`, `Number`, `Math.max`). */
|
|
50
|
+
calls: string[];
|
|
51
|
+
externals: string[];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** A value position: a leaf of the structure. `text` is always the authored text. */
|
|
55
|
+
export type TransformValue =
|
|
56
|
+
| { kind: 'literal'; text: string }
|
|
57
|
+
| { kind: 'input'; text: string; input: TransformInputRef }
|
|
58
|
+
| { kind: 'factor'; text: string; name: string }
|
|
59
|
+
| {
|
|
60
|
+
kind: 'call';
|
|
61
|
+
text: string;
|
|
62
|
+
/**
|
|
63
|
+
* The name at the call position. WITHOUT `receiver` it is a function a
|
|
64
|
+
* reader can look up (`employeeFloor`, `Number`, `Math.max`). WITH
|
|
65
|
+
* `receiver` it is a METHOD name and means nothing on its own: `trim` is
|
|
66
|
+
* not a function anybody can find, it is what was done to the receiver.
|
|
67
|
+
*/
|
|
68
|
+
callee: string;
|
|
69
|
+
args: TransformValue[];
|
|
70
|
+
/**
|
|
71
|
+
* The value the method was reached THROUGH, when the call is a method
|
|
72
|
+
* call — `String(row.domain)` in `String(row.domain).trim()`.
|
|
73
|
+
*
|
|
74
|
+
* Absent for a plain named call, and the absence is load-bearing: a
|
|
75
|
+
* consumer that evaluates the language's own coercions (`Number`,
|
|
76
|
+
* `String`, `Boolean`) must not fire on `x.Number(y)`, which is somebody
|
|
77
|
+
* else's method that happens to share a name.
|
|
78
|
+
*/
|
|
79
|
+
receiver?: TransformValue;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A property reached through another value — `profile.company.name`,
|
|
83
|
+
* `parts[0]`, `String(row.domain).length`.
|
|
84
|
+
*
|
|
85
|
+
* Only a STATIC key: a name, or a literal index. `row[key]` names a property
|
|
86
|
+
* nobody can read off the source, and it stays verbatim rather than being
|
|
87
|
+
* described as a property called `key`.
|
|
88
|
+
*
|
|
89
|
+
* `optional` is the author's `?.`, and it is carried because it changes what
|
|
90
|
+
* happens on a missing object: with it the read yields undefined, without it
|
|
91
|
+
* the run threw. A consumer that flattened the two would state a value for a
|
|
92
|
+
* row whose run never got this far.
|
|
93
|
+
*/
|
|
94
|
+
| {
|
|
95
|
+
kind: 'member';
|
|
96
|
+
text: string;
|
|
97
|
+
object: TransformValue;
|
|
98
|
+
property: string;
|
|
99
|
+
optional: boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A fallback chain — `row.name ?? row.legal_name ?? null`.
|
|
103
|
+
*
|
|
104
|
+
* The operands are in the order the author wrote them, which is the order
|
|
105
|
+
* they are tried in. Nothing here says which one won: that depends on the
|
|
106
|
+
* row, and deciding it is the consumer's job over recorded values, exactly as
|
|
107
|
+
* it is for a conditional's arms.
|
|
108
|
+
*/
|
|
109
|
+
| {
|
|
110
|
+
kind: 'coalesce';
|
|
111
|
+
operator: TransformCoalesceOperator;
|
|
112
|
+
text: string;
|
|
113
|
+
operands: TransformValue[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A template literal, as the described parts it joins —
|
|
117
|
+
* `` `${row.first_name} ${row.last_name}` `` is three parts, the middle one a
|
|
118
|
+
* literal space.
|
|
119
|
+
*
|
|
120
|
+
* Only an UNTAGGED template. A tagged one runs the tag's body over the parts,
|
|
121
|
+
* and that body is not in this structure, so it stays verbatim.
|
|
122
|
+
*/
|
|
123
|
+
| { kind: 'concat'; text: string; parts: TransformValue[] }
|
|
124
|
+
/**
|
|
125
|
+
* An identifier bound outside the transform — a module constant, an import.
|
|
126
|
+
*
|
|
127
|
+
* NAMED, never valued. The extractor reads one lambda; what a module constant
|
|
128
|
+
* holds at run time is not in it, and a leaf that claimed to know would be
|
|
129
|
+
* guessing. Distinct from `expression` because there IS something to say
|
|
130
|
+
* here: a reader can go and look this name up.
|
|
131
|
+
*/
|
|
132
|
+
| { kind: 'external'; text: string; name: string }
|
|
133
|
+
/** Anything else, carried verbatim. Never paraphrased. */
|
|
134
|
+
| { kind: 'expression'; text: string };
|
|
135
|
+
|
|
136
|
+
export type TransformComparisonOperator =
|
|
137
|
+
| '==='
|
|
138
|
+
| '!=='
|
|
139
|
+
| '=='
|
|
140
|
+
| '!='
|
|
141
|
+
| '>='
|
|
142
|
+
| '>'
|
|
143
|
+
| '<='
|
|
144
|
+
| '<';
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A test in a conditional.
|
|
148
|
+
*
|
|
149
|
+
* `truthiness` is the bare `row.headquarters ? 15 : 0` shape — the author asked
|
|
150
|
+
* whether a value is there at all, and the renderer says exactly that rather
|
|
151
|
+
* than inventing a comparison the code does not make.
|
|
152
|
+
*/
|
|
153
|
+
export type TransformCondition =
|
|
154
|
+
| {
|
|
155
|
+
kind: 'comparison';
|
|
156
|
+
text: string;
|
|
157
|
+
left: TransformValue;
|
|
158
|
+
operator: TransformComparisonOperator;
|
|
159
|
+
right: TransformValue;
|
|
160
|
+
}
|
|
161
|
+
| {
|
|
162
|
+
kind: 'truthiness';
|
|
163
|
+
text: string;
|
|
164
|
+
subject: TransformValue;
|
|
165
|
+
negated: boolean;
|
|
166
|
+
}
|
|
167
|
+
| { kind: 'all'; text: string; parts: TransformCondition[] }
|
|
168
|
+
| { kind: 'any'; text: string; parts: TransformCondition[] }
|
|
169
|
+
/** Structurally unreadable, carried verbatim. */
|
|
170
|
+
| { kind: 'opaque'; text: string };
|
|
171
|
+
|
|
172
|
+
/** One arm of a ternary chain: this condition selects this value. */
|
|
173
|
+
export type TransformConditionArm = {
|
|
174
|
+
condition: TransformCondition;
|
|
175
|
+
value: TransformValue;
|
|
176
|
+
/** What this arm's test and value read. */
|
|
177
|
+
refs: TransformRefs;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** Arithmetic operators the extractor will flatten into an operand list. */
|
|
181
|
+
export type TransformCombinationOperator = '+' | '-' | '*' | '/' | '%';
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The two fallback operators, kept APART because they ask different questions.
|
|
185
|
+
*
|
|
186
|
+
* `??` takes the first operand that is neither null nor undefined. `||` takes
|
|
187
|
+
* the first TRUTHY one, so it also steps over `0`, `''` and `false` — values an
|
|
188
|
+
* author may well have meant to keep. Describing both as "first present of"
|
|
189
|
+
* would state the wrong rule for half the corpus, so the operator travels with
|
|
190
|
+
* the structure and every renderer says which one it is looking at.
|
|
191
|
+
*/
|
|
192
|
+
export type TransformCoalesceOperator = '??' | '||';
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* How a factor (or the transform's result) gets its value.
|
|
196
|
+
*
|
|
197
|
+
* `value` with an `expression` leaf is the marked-partial case: the rule was
|
|
198
|
+
* not structurable, and the factor carries `opaque: true` so the renderer
|
|
199
|
+
* prints the author's text instead of a description of it.
|
|
200
|
+
*/
|
|
201
|
+
export type TransformRule =
|
|
202
|
+
| { kind: 'value'; value: TransformValue }
|
|
203
|
+
| {
|
|
204
|
+
kind: 'conditional';
|
|
205
|
+
arms: TransformConditionArm[];
|
|
206
|
+
otherwise: TransformValue;
|
|
207
|
+
}
|
|
208
|
+
| {
|
|
209
|
+
kind: 'combination';
|
|
210
|
+
operator: TransformCombinationOperator;
|
|
211
|
+
operands: TransformValue[];
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/** One named intermediate step: a `const` in the transform body. */
|
|
215
|
+
export type TransformFactor = {
|
|
216
|
+
name: string;
|
|
217
|
+
/** The authored initializer, whitespace-normalized. Always renderable. */
|
|
218
|
+
expression: string;
|
|
219
|
+
rule: TransformRule;
|
|
220
|
+
/** Union of everything the initializer reads. */
|
|
221
|
+
refs: TransformRefs;
|
|
222
|
+
/** True when the rule is verbatim text rather than a described structure. */
|
|
223
|
+
opaque?: true;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/** The transform's returned value. */
|
|
227
|
+
export type TransformResult = {
|
|
228
|
+
expression: string;
|
|
229
|
+
rule: TransformRule;
|
|
230
|
+
refs: TransformRefs;
|
|
231
|
+
opaque?: true;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The extracted structure of one column's transform.
|
|
236
|
+
*
|
|
237
|
+
* `inputColumns` is the join key for the UI: the columns this transform reads,
|
|
238
|
+
* in first-appearance order, so factor lines can be sat beside the trace's own
|
|
239
|
+
* input hops without the renderer re-deriving them.
|
|
240
|
+
*/
|
|
241
|
+
export type TransformFactorStructure = {
|
|
242
|
+
/** Dataset column this transform produces. */
|
|
243
|
+
column: string;
|
|
244
|
+
/** Dataset the column belongs to, when the extractor could name it. */
|
|
245
|
+
dataset?: string;
|
|
246
|
+
/** The transform's row parameter name, as authored. */
|
|
247
|
+
rowParam: string;
|
|
248
|
+
factors: TransformFactor[];
|
|
249
|
+
result: TransformResult;
|
|
250
|
+
inputColumns: string[];
|
|
251
|
+
/** Functions called by the transform, by written name. */
|
|
252
|
+
helpers: string[];
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A structure, addressed the way a trace addresses cells.
|
|
257
|
+
*
|
|
258
|
+
* The structure alone says what a transform IS; it does not say which cell it
|
|
259
|
+
* explains. A consumer holding a chain of hops needs both, so the address
|
|
260
|
+
* travels with it — table and column, verbatim as the trace reported them, so
|
|
261
|
+
* the join is a string match and never a guess about namespacing.
|
|
262
|
+
*/
|
|
263
|
+
export type TransformFactorEntry = {
|
|
264
|
+
table: string;
|
|
265
|
+
column: string;
|
|
266
|
+
structure: TransformFactorStructure;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Why no structure was produced.
|
|
271
|
+
*
|
|
272
|
+
* Each value names a shape the extractor refuses to describe rather than
|
|
273
|
+
* describe badly. `unsupported_statement` covers loops, branches, mutation and
|
|
274
|
+
* try/catch in one: the supported body is a run of `const` declarations
|
|
275
|
+
* followed by a `return`, and anything else in it means the description would
|
|
276
|
+
* omit real behavior.
|
|
277
|
+
*
|
|
278
|
+
* The last two are about the extractor rather than the code.
|
|
279
|
+
* `expression_too_deep` is a nesting budget: a described value that nests past
|
|
280
|
+
* it is refused instead of risking the stack. `extraction_failed` is the net
|
|
281
|
+
* under everything — any error the extractor did not anticipate arrives here
|
|
282
|
+
* with its message, so a surface reports "cannot read this" instead of failing
|
|
283
|
+
* the request. Either one appearing on an authored play is a bug worth chasing.
|
|
284
|
+
*/
|
|
285
|
+
export type TransformUnstructurableReason =
|
|
286
|
+
| 'column_not_found'
|
|
287
|
+
| 'ambiguous_column'
|
|
288
|
+
| 'not_a_function'
|
|
289
|
+
/**
|
|
290
|
+
* The column is filled by a `steps()` program — a provider cascade — and not
|
|
291
|
+
* by code. There are no factors to describe because there is no arithmetic:
|
|
292
|
+
* the trace explains such a cell with its hops, which is a better answer than
|
|
293
|
+
* anything this module could say about it.
|
|
294
|
+
*/
|
|
295
|
+
| 'step_program_column'
|
|
296
|
+
| 'async_transform'
|
|
297
|
+
| 'destructured_row_param'
|
|
298
|
+
| 'row_context_used'
|
|
299
|
+
| 'row_index_used'
|
|
300
|
+
/**
|
|
301
|
+
* The transform read `previousCell` — the value this cell held before the
|
|
302
|
+
* rerun — which the object column form hands its `run` alongside the row.
|
|
303
|
+
*
|
|
304
|
+
* Not a column, and named separately from the row context and the row index
|
|
305
|
+
* for the same reason those two are named separately from each other: a
|
|
306
|
+
* refusal that misnames what it refused sends the next reader looking in the
|
|
307
|
+
* wrong place.
|
|
308
|
+
*/
|
|
309
|
+
| 'previous_cell_used'
|
|
310
|
+
/**
|
|
311
|
+
* An object column's `run` was handed its input WHOLE — `(input) =>
|
|
312
|
+
* input.row.domain` rather than `({ row }) => row.domain`.
|
|
313
|
+
*
|
|
314
|
+
* The contract passes `run` one argument, `{ row, ctx, index, previousCell }`,
|
|
315
|
+
* so the row is a property of the parameter rather than the parameter. This
|
|
316
|
+
* module names a row PARAMETER and reads properties off it; a row one level
|
|
317
|
+
* further in is a shape it cannot describe, and describing the envelope
|
|
318
|
+
* instead would report `row` and `ctx` as if they were dataset columns.
|
|
319
|
+
*/
|
|
320
|
+
| 'run_input_not_destructured'
|
|
321
|
+
| 'nested_function'
|
|
322
|
+
| 'unsupported_statement'
|
|
323
|
+
| 'unsupported_declaration'
|
|
324
|
+
| 'missing_return'
|
|
325
|
+
| 'dynamic_row_access'
|
|
326
|
+
| 'expression_too_deep'
|
|
327
|
+
| 'extraction_failed';
|
|
328
|
+
|
|
329
|
+
/** Extraction outcome. A failure is a fact with a reason, never an exception. */
|
|
330
|
+
export type TransformFactorExtraction =
|
|
331
|
+
| { structure: TransformFactorStructure; reason: null }
|
|
332
|
+
| { structure: null; reason: TransformUnstructurableReason; detail?: string };
|
|
333
|
+
|
|
334
|
+
/** Empty ref set. Callers merge into a copy; this value is never mutated. */
|
|
335
|
+
export function emptyTransformRefs(): TransformRefs {
|
|
336
|
+
return { inputs: [], factors: [], calls: [], externals: [] };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Merge ref sets, preserving first-appearance order and deduping. */
|
|
340
|
+
export function mergeTransformRefs(
|
|
341
|
+
...sets: readonly TransformRefs[]
|
|
342
|
+
): TransformRefs {
|
|
343
|
+
const merged = emptyTransformRefs();
|
|
344
|
+
const seenInputs = new Set<string>();
|
|
345
|
+
const seenFactors = new Set<string>();
|
|
346
|
+
const seenCalls = new Set<string>();
|
|
347
|
+
const seenExternals = new Set<string>();
|
|
348
|
+
for (const set of sets) {
|
|
349
|
+
for (const input of set.inputs) {
|
|
350
|
+
const key = transformInputKey(input);
|
|
351
|
+
if (seenInputs.has(key)) continue;
|
|
352
|
+
seenInputs.add(key);
|
|
353
|
+
merged.inputs.push(input);
|
|
354
|
+
}
|
|
355
|
+
for (const factor of set.factors) {
|
|
356
|
+
if (seenFactors.has(factor)) continue;
|
|
357
|
+
seenFactors.add(factor);
|
|
358
|
+
merged.factors.push(factor);
|
|
359
|
+
}
|
|
360
|
+
for (const call of set.calls) {
|
|
361
|
+
if (seenCalls.has(call)) continue;
|
|
362
|
+
seenCalls.add(call);
|
|
363
|
+
merged.calls.push(call);
|
|
364
|
+
}
|
|
365
|
+
for (const external of set.externals) {
|
|
366
|
+
if (seenExternals.has(external)) continue;
|
|
367
|
+
seenExternals.add(external);
|
|
368
|
+
merged.externals.push(external);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return merged;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Stable identity for an input ref, column plus path. */
|
|
375
|
+
export function transformInputKey(input: TransformInputRef): string {
|
|
376
|
+
return [input.column, ...(input.path ?? [])].join('.');
|
|
377
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const WEBHOOK_KEY_PREFIX = '__deepline_webhook_key_v1_';
|
|
2
|
+
/**
|
|
3
|
+
* Play inputs are objects or arrays. Rather than acknowledge a scalar JSON or
|
|
4
|
+
* non-JSON delivery and silently replace it with `{}`, retain the exact body
|
|
5
|
+
* in a namespaced input envelope. This name is only introduced for payloads
|
|
6
|
+
* which cannot themselves be a Play input, so it cannot collide with a key in
|
|
7
|
+
* a caller-provided object.
|
|
8
|
+
*/
|
|
9
|
+
export const WEBHOOK_RAW_INPUT_KEY = '__deepline_webhook_raw_v1';
|
|
10
|
+
const encoder = new TextEncoder();
|
|
11
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
12
|
+
|
|
13
|
+
export type PlayWebhookPayload = Record<string, unknown> | unknown[];
|
|
14
|
+
|
|
15
|
+
export type RetainedWebhookPayload = {
|
|
16
|
+
rawBody: string;
|
|
17
|
+
contentType: string | null;
|
|
18
|
+
encoding: 'utf-8';
|
|
19
|
+
/** Present when the body was valid JSON but not an object or array. */
|
|
20
|
+
jsonValue?: string | number | boolean | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert an arbitrary HTTP body into a valid Play input without losing it.
|
|
25
|
+
* Object and array JSON remain byte-for-byte equivalent at the JSON value
|
|
26
|
+
* level; scalar JSON and non-JSON use a deliberately explicit envelope.
|
|
27
|
+
*/
|
|
28
|
+
export function parsePlayWebhookPayload(input: {
|
|
29
|
+
rawBody: string;
|
|
30
|
+
contentType?: string | null;
|
|
31
|
+
}): PlayWebhookPayload {
|
|
32
|
+
const contentType = input.contentType?.trim() || null;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(input.rawBody) as unknown;
|
|
35
|
+
if (Array.isArray(parsed)) return parsed;
|
|
36
|
+
if (parsed && typeof parsed === 'object') {
|
|
37
|
+
return parsed as Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
parsed === null ||
|
|
41
|
+
typeof parsed === 'string' ||
|
|
42
|
+
typeof parsed === 'number' ||
|
|
43
|
+
typeof parsed === 'boolean'
|
|
44
|
+
) {
|
|
45
|
+
return {
|
|
46
|
+
[WEBHOOK_RAW_INPUT_KEY]: {
|
|
47
|
+
rawBody: input.rawBody,
|
|
48
|
+
contentType,
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
jsonValue: parsed,
|
|
51
|
+
} satisfies RetainedWebhookPayload,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// The raw body below is the durable, user-visible value for non-JSON.
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
[WEBHOOK_RAW_INPUT_KEY]: {
|
|
59
|
+
rawBody: input.rawBody,
|
|
60
|
+
contentType,
|
|
61
|
+
encoding: 'utf-8',
|
|
62
|
+
} satisfies RetainedWebhookPayload,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function encodeKey(key: string): string {
|
|
67
|
+
return (
|
|
68
|
+
WEBHOOK_KEY_PREFIX +
|
|
69
|
+
Array.from(encoder.encode(key), (byte) =>
|
|
70
|
+
byte.toString(16).padStart(2, '0'),
|
|
71
|
+
).join('')
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function decodeKey(key: string): string | null {
|
|
76
|
+
if (!key.startsWith(WEBHOOK_KEY_PREFIX)) return null;
|
|
77
|
+
const encoded = key.slice(WEBHOOK_KEY_PREFIX.length);
|
|
78
|
+
if (!encoded || encoded.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(encoded)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const bytes = new Uint8Array(
|
|
83
|
+
encoded.match(/.{2}/g)!.map((pair) => Number.parseInt(pair, 16)),
|
|
84
|
+
);
|
|
85
|
+
const decoded = decoder.decode(bytes);
|
|
86
|
+
return encodeKey(decoded) === key ? decoded : null;
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function mapKeys(value: unknown, mapKey: (key: string) => string): unknown {
|
|
93
|
+
if (Array.isArray(value)) return value.map((item) => mapKeys(item, mapKey));
|
|
94
|
+
if (!value || typeof value !== 'object') return value;
|
|
95
|
+
return Object.fromEntries(
|
|
96
|
+
Object.entries(value as Record<string, unknown>).map(
|
|
97
|
+
([key, nestedValue]) => [mapKey(key), mapKeys(nestedValue, mapKey)],
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Convex only accepts ASCII object keys. Webhook bodies are external JSON, so
|
|
104
|
+
* encode every key losslessly before crossing its client or persistence boundary.
|
|
105
|
+
*/
|
|
106
|
+
export function encodeWebhookPayloadForConvex(value: unknown): unknown {
|
|
107
|
+
return mapKeys(value, encodeKey);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Restores webhook payload keys before they are made available to a Play. */
|
|
111
|
+
export function decodeWebhookPayloadFromConvex(value: unknown): unknown {
|
|
112
|
+
return mapKeys(value, (key) => decodeKey(key) ?? key);
|
|
113
|
+
}
|