@pattern-stack/codegen 0.13.0 → 0.13.1
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/runtime/subsystems/index.d.ts +1 -1
- package/dist/runtime/subsystems/index.js +9 -6
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/integration/incremental-read.d.ts +35 -8
- package/dist/runtime/subsystems/integration/incremental-read.js +9 -6
- package/dist/runtime/subsystems/integration/incremental-read.js.map +1 -1
- package/dist/runtime/subsystems/integration/index.d.ts +1 -1
- package/dist/runtime/subsystems/integration/index.js +9 -6
- package/dist/runtime/subsystems/integration/index.js.map +1 -1
- package/dist/src/cli/index.js +13 -0
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/subsystems/index.ts +1 -0
- package/runtime/subsystems/integration/incremental-read.ts +43 -9
- package/runtime/subsystems/integration/index.ts +1 -0
package/package.json
CHANGED
|
@@ -79,6 +79,26 @@ export interface ReadRequest<F = unknown> {
|
|
|
79
79
|
readonly pageSize?: number;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Per-run context threaded from `listChanges` into the vendor read body (R5).
|
|
84
|
+
*
|
|
85
|
+
* Carries the `subscription` framing the run so `enumerate`/`hydrate` can resolve
|
|
86
|
+
* **per-connection credentials** (and raw-landing keys) from
|
|
87
|
+
* `subscription.externalRef` — the gap a multi-account consumer surfaced: a
|
|
88
|
+
* singleton change source cannot hold connection-scoped auth, and before R5 the
|
|
89
|
+
* base forwarded the subscription only into `filterFor`, never into the fetch.
|
|
90
|
+
*
|
|
91
|
+
* Optional throughout (the core contract): a direct `read()` / `get()` call — the
|
|
92
|
+
* query surface's "fill one record on click" — may omit it. An adapter that needs
|
|
93
|
+
* per-connection auth reads `ctx?.subscription?.externalRef` and asserts its
|
|
94
|
+
* presence; a provider-level-auth adapter ignores it.
|
|
95
|
+
*/
|
|
96
|
+
export interface ReadContext {
|
|
97
|
+
/** The subscription framing this run; `externalRef` is the upstream scope /
|
|
98
|
+
* connection id the adapter resolves credentials + raw-landing keys from. */
|
|
99
|
+
readonly subscription?: IntegrationSubscriptionView;
|
|
100
|
+
}
|
|
101
|
+
|
|
82
102
|
/**
|
|
83
103
|
* The `read()`-side envelope: canonical record + the raw vendor payload it came
|
|
84
104
|
* from + the originating external id + the per-ref cursor.
|
|
@@ -101,7 +121,7 @@ export interface SourcedRecord<T> {
|
|
|
101
121
|
* hydration, and cursor emission are the providing base's concern.
|
|
102
122
|
*/
|
|
103
123
|
export interface IncrementalRead<T, F = unknown> {
|
|
104
|
-
read(req: ReadRequest<F
|
|
124
|
+
read(req: ReadRequest<F>, ctx?: ReadContext): AsyncIterable<SourcedRecord<T>>;
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
/**
|
|
@@ -111,7 +131,7 @@ export interface IncrementalRead<T, F = unknown> {
|
|
|
111
131
|
* surface.
|
|
112
132
|
*/
|
|
113
133
|
export interface RandomRead<T> {
|
|
114
|
-
get(id: string): Promise<T | null>;
|
|
134
|
+
get(id: string, ctx?: ReadContext): Promise<T | null>;
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
// ============================================================================
|
|
@@ -208,11 +228,16 @@ export abstract class IncrementalReadBase<T, F = unknown, M = Record<string, unk
|
|
|
208
228
|
* `pageSize` (from `ReadRequest`) is the adapter's requested vendor page size
|
|
209
229
|
* — also the atomic-cursor backfill blast-radius bound (§ `cursorDivisible`).
|
|
210
230
|
* Honor it as a hint; vendors that cap page size clamp it.
|
|
231
|
+
*
|
|
232
|
+
* `ctx?.subscription` (R5) carries the run's subscription, so a per-connection
|
|
233
|
+
* adapter resolves credentials / upstream scope from `externalRef` here; absent
|
|
234
|
+
* on a direct `read()` with no run subscription.
|
|
211
235
|
*/
|
|
212
236
|
protected abstract enumerate(
|
|
213
237
|
mode: ReadMode,
|
|
214
238
|
filter?: F,
|
|
215
239
|
pageSize?: number,
|
|
240
|
+
ctx?: ReadContext,
|
|
216
241
|
): AsyncIterable<Ref<M>[]>;
|
|
217
242
|
|
|
218
243
|
/**
|
|
@@ -221,8 +246,12 @@ export abstract class IncrementalReadBase<T, F = unknown, M = Record<string, unk
|
|
|
221
246
|
* alignment. Write it over `mapConcurrent(ids, (id) => this.fetchOne(id),
|
|
222
247
|
* this.hydrateConcurrency)`; override with a real `/batch` call or a
|
|
223
248
|
* passthrough (full-object list) where the vendor allows.
|
|
249
|
+
*
|
|
250
|
+
* `ctx?.subscription` (R5) carries the run's subscription for per-connection
|
|
251
|
+
* credential resolution (the fetch is where the vendor call happens) and is the
|
|
252
|
+
* natural place to land raw payloads keyed by `subscription.id`.
|
|
224
253
|
*/
|
|
225
|
-
protected abstract hydrate(ids: string[]): Promise<Map<string, unknown>>;
|
|
254
|
+
protected abstract hydrate(ids: string[], ctx?: ReadContext): Promise<Map<string, unknown>>;
|
|
226
255
|
|
|
227
256
|
/** Provider payload → canonical record. Return `null` to drop a record. */
|
|
228
257
|
protected abstract toCanonical(raw: unknown): T | null;
|
|
@@ -256,11 +285,14 @@ export abstract class IncrementalReadBase<T, F = unknown, M = Record<string, unk
|
|
|
256
285
|
* adapter cannot hydrate-then-discard. A hydrate miss (deleted mid-run) is
|
|
257
286
|
* skipped, never fabricated.
|
|
258
287
|
*/
|
|
259
|
-
async *read(req: ReadRequest<F
|
|
260
|
-
for await (const refPage of this.enumerate(req.mode, req.filter, req.pageSize)) {
|
|
288
|
+
async *read(req: ReadRequest<F>, ctx?: ReadContext): AsyncIterable<SourcedRecord<T>> {
|
|
289
|
+
for await (const refPage of this.enumerate(req.mode, req.filter, req.pageSize, ctx)) {
|
|
261
290
|
const kept = refPage.filter((ref) => this.matchesRef(ref, req.filter));
|
|
262
291
|
if (kept.length === 0) continue;
|
|
263
|
-
const raws = await this.hydrate(
|
|
292
|
+
const raws = await this.hydrate(
|
|
293
|
+
kept.map((ref) => ref.externalId),
|
|
294
|
+
ctx,
|
|
295
|
+
);
|
|
264
296
|
for (const ref of kept) {
|
|
265
297
|
const raw = raws.get(ref.externalId);
|
|
266
298
|
if (raw === undefined || raw === null) continue; // deleted mid-run → skip
|
|
@@ -277,8 +309,8 @@ export abstract class IncrementalReadBase<T, F = unknown, M = Record<string, unk
|
|
|
277
309
|
* `toCanonical ∘ hydrate([id])`. Reuses the adapter's batched fetch + miss
|
|
278
310
|
* tolerance; returns `null` for a missing or undecodable record.
|
|
279
311
|
*/
|
|
280
|
-
async get(id: string): Promise<T | null> {
|
|
281
|
-
const raws = await this.hydrate([id]);
|
|
312
|
+
async get(id: string, ctx?: ReadContext): Promise<T | null> {
|
|
313
|
+
const raws = await this.hydrate([id], ctx);
|
|
282
314
|
const raw = raws.get(id);
|
|
283
315
|
if (raw === undefined || raw === null) return null;
|
|
284
316
|
return this.toCanonical(raw);
|
|
@@ -308,7 +340,9 @@ export abstract class IncrementalReadBase<T, F = unknown, M = Record<string, unk
|
|
|
308
340
|
? { kind: 'full' }
|
|
309
341
|
: { kind: 'delta', cursor };
|
|
310
342
|
const filter = this.filterFor(subscription);
|
|
311
|
-
|
|
343
|
+
// R5: thread the run's subscription into the read body so `enumerate`/`hydrate`
|
|
344
|
+
// can resolve per-connection credentials (and raw-landing keys) from it.
|
|
345
|
+
const stream = this.read({ mode, filter }, { subscription });
|
|
312
346
|
|
|
313
347
|
if (this.cursorDivisible) {
|
|
314
348
|
for await (const sourced of stream) {
|