monday-cli 0.3.0 → 0.4.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/CHANGELOG.md +351 -0
- package/README.md +130 -36
- package/dist/api/assets.d.ts +326 -0
- package/dist/api/assets.d.ts.map +1 -0
- package/dist/api/assets.js +519 -0
- package/dist/api/assets.js.map +1 -0
- package/dist/api/column-types.d.ts +11 -7
- package/dist/api/column-types.d.ts.map +1 -1
- package/dist/api/column-types.js +5 -3
- package/dist/api/column-types.js.map +1 -1
- package/dist/api/column-values.d.ts +7 -1
- package/dist/api/column-values.d.ts.map +1 -1
- package/dist/api/column-values.js +15 -6
- package/dist/api/column-values.js.map +1 -1
- package/dist/api/documents.d.ts +519 -0
- package/dist/api/documents.d.ts.map +1 -0
- package/dist/api/documents.js +586 -0
- package/dist/api/documents.js.map +1 -0
- package/dist/api/item-watch.d.ts +263 -0
- package/dist/api/item-watch.d.ts.map +1 -0
- package/dist/api/item-watch.js +709 -0
- package/dist/api/item-watch.js.map +1 -0
- package/dist/api/multipart-transport.d.ts +223 -0
- package/dist/api/multipart-transport.d.ts.map +1 -0
- package/dist/api/multipart-transport.js +274 -0
- package/dist/api/multipart-transport.js.map +1 -0
- package/dist/api/parallel-dispatch.d.ts +155 -0
- package/dist/api/parallel-dispatch.d.ts.map +1 -0
- package/dist/api/parallel-dispatch.js +243 -0
- package/dist/api/parallel-dispatch.js.map +1 -0
- package/dist/api/partial-success-bulk.d.ts +118 -60
- package/dist/api/partial-success-bulk.d.ts.map +1 -1
- package/dist/api/partial-success-bulk.js +137 -79
- package/dist/api/partial-success-bulk.js.map +1 -1
- package/dist/api/partial-success-mutation.d.ts +13 -1
- package/dist/api/partial-success-mutation.d.ts.map +1 -1
- package/dist/api/partial-success-mutation.js +5 -1
- package/dist/api/partial-success-mutation.js.map +1 -1
- package/dist/api/raw-write.d.ts +12 -4
- package/dist/api/raw-write.d.ts.map +1 -1
- package/dist/api/raw-write.js +21 -11
- package/dist/api/raw-write.js.map +1 -1
- package/dist/api/resolve-client.d.ts +11 -0
- package/dist/api/resolve-client.d.ts.map +1 -1
- package/dist/api/resolve-client.js +9 -1
- package/dist/api/resolve-client.js.map +1 -1
- package/dist/cli/run.d.ts +20 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +1 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/commands/board/column-create.d.ts +6 -5
- package/dist/commands/board/column-create.d.ts.map +1 -1
- package/dist/commands/board/column-create.js +9 -6
- package/dist/commands/board/column-create.js.map +1 -1
- package/dist/commands/completion.d.ts +188 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +418 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/doc/get.d.ts +46 -0
- package/dist/commands/doc/get.d.ts.map +1 -0
- package/dist/commands/doc/get.js +95 -0
- package/dist/commands/doc/get.js.map +1 -0
- package/dist/commands/doc/list.d.ts +83 -0
- package/dist/commands/doc/list.d.ts.map +1 -0
- package/dist/commands/doc/list.js +248 -0
- package/dist/commands/doc/list.js.map +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +46 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/item/create.js +2 -2
- package/dist/commands/item/update.d.ts +1 -0
- package/dist/commands/item/update.d.ts.map +1 -1
- package/dist/commands/item/update.js +61 -0
- package/dist/commands/item/update.js.map +1 -1
- package/dist/commands/item/upload.d.ts +108 -0
- package/dist/commands/item/upload.d.ts.map +1 -0
- package/dist/commands/item/upload.js +370 -0
- package/dist/commands/item/upload.js.map +1 -0
- package/dist/commands/item/watch.d.ts +90 -0
- package/dist/commands/item/watch.d.ts.map +1 -0
- package/dist/commands/item/watch.js +342 -0
- package/dist/commands/item/watch.js.map +1 -0
- package/dist/commands/update/upload.d.ts +69 -0
- package/dist/commands/update/upload.d.ts.map +1 -0
- package/dist/commands/update/upload.js +235 -0
- package/dist/commands/update/upload.js.map +1 -0
- package/dist/types/ids.d.ts +2 -0
- package/dist/types/ids.d.ts.map +1 -1
- package/dist/types/ids.js +9 -2
- package/dist/types/ids.js.map +1 -1
- package/dist/utils/mime.d.ts +24 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +64 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/output/envelope.d.ts +30 -0
- package/dist/utils/output/envelope.d.ts.map +1 -1
- package/dist/utils/output/envelope.js +26 -0
- package/dist/utils/output/envelope.js.map +1 -1
- package/dist/utils/output/ndjson.d.ts +25 -0
- package/dist/utils/output/ndjson.d.ts.map +1 -1
- package/dist/utils/output/ndjson.js +12 -0
- package/dist/utils/output/ndjson.js.map +1 -1
- package/dist/utils/signal.d.ts +42 -0
- package/dist/utils/signal.d.ts.map +1 -0
- package/dist/utils/signal.js +45 -0
- package/dist/utils/signal.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
* `item update --continue-on-error` flag (`cli-design.md` §6.4
|
|
4
4
|
* "Bulk per-item partial-success" sub-section).
|
|
5
5
|
*
|
|
6
|
-
* **What this module owns.** A thin wrapper around
|
|
7
|
-
* {@link dispatchSequential} from
|
|
8
|
-
* `src/api/partial-success-mutation.ts`
|
|
6
|
+
* **What this module owns.** A thin wrapper around the selected
|
|
7
|
+
* per-target dispatcher — {@link dispatchSequential} from
|
|
8
|
+
* `src/api/partial-success-mutation.ts` (default / M25 path) OR
|
|
9
|
+
* {@link dispatchParallel} from `src/api/parallel-dispatch.ts`
|
|
10
|
+
* (v0.4-M30 `--concurrency > 1` path) — that drives the matched-
|
|
9
11
|
* item-ID list through one wire call per item, capturing per-
|
|
10
12
|
* item failures into the result records rather than aborting
|
|
11
13
|
* the loop. The wrapper sits BETWEEN the bulk command-action
|
|
12
14
|
* orchestrator (`src/commands/item/update.ts:runBulk`) and the
|
|
13
|
-
* shared
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* shared dispatcher — the action body owns the matched-item-
|
|
16
|
+
* walk + column-resolution pre-pass + confirmation gate, then
|
|
17
|
+
* hands the resolved `SelectedMutation` + matched-item IDs to
|
|
18
|
+
* this wrapper, which fans them out + projects the partial-
|
|
19
|
+
* success envelope's `data.results[]` records.
|
|
18
20
|
*
|
|
19
21
|
* **Why a separate module rather than folding into update.ts.**
|
|
20
22
|
* Three reasons:
|
|
@@ -65,16 +67,31 @@
|
|
|
65
67
|
* 3-consumer trigger: single-item + fail-fast bulk + M25
|
|
66
68
|
* partial-success bulk).
|
|
67
69
|
*
|
|
68
|
-
* **
|
|
69
|
-
* {@link
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
70
|
+
* **v0.4-M30 extension.** Adds the `concurrency` input slot +
|
|
71
|
+
* the routing branch to {@link dispatchParallel} (new module
|
|
72
|
+
* `src/api/parallel-dispatch.ts` — runtime body landed at M30
|
|
73
|
+
* IMPL). When the caller passes `concurrency > 1`, the runtime
|
|
74
|
+
* fans out per-target dispatches via a bounded async-pool;
|
|
75
|
+
* absent or `concurrency === 1` preserves the M25 sequential
|
|
76
|
+
* path verbatim. The per-target dispatch closure is hoisted to
|
|
77
|
+
* a named local so both routes share the same
|
|
78
|
+
* `executeItemMutation` + `foldAndRemap` body — keeps the
|
|
79
|
+
* R-NEW-28 6-axis behavioral-equivalence audit straightforward.
|
|
80
|
+
* The M30 IMPL also threads an optional `signal?: AbortSignal`
|
|
81
|
+
* through both dispatchers (axis-6 scheduler short-circuit).
|
|
82
|
+
*
|
|
83
|
+
* **Per-item dispatch wiring.** Runtime body routes between
|
|
84
|
+
* {@link dispatchSequential} (default — `concurrency` absent /
|
|
85
|
+
* `=== 1`) and {@link dispatchParallel} (v0.4-M30 —
|
|
86
|
+
* `concurrency > 1`) over `matchedItemIds` with id-field
|
|
87
|
+
* `'item_id'`. The per-target dispatch callback (shared between
|
|
88
|
+
* routes verbatim) fires one `executeItemMutation` call.
|
|
89
|
+
* Successes populate `results[i].item` with the `ProjectedItem`
|
|
90
|
+
* via a side-map fold; failures land in
|
|
91
|
+
* `results[i].error: {code, message}` via the dispatcher's
|
|
92
|
+
* built-in error decoration. `internal_error` codes re-throw
|
|
93
|
+
* as whole-call (M14 round-2 F1 precedent — schema-drift in
|
|
94
|
+
* the response MUST NOT be papered over as a per-item failure).
|
|
78
95
|
*
|
|
79
96
|
* **`data.summary.failed_count` invariant.** The action body
|
|
80
97
|
* derives `failed_count` from the result records
|
|
@@ -103,13 +120,15 @@ import type { EnvelopeSource } from './source-aggregator.js';
|
|
|
103
120
|
* The `item` slot on success records is the §6.2 `ProjectedItem`
|
|
104
121
|
* shape (same projection single-item `item update` emits as
|
|
105
122
|
* `data`). The `error` slot on failure records carries
|
|
106
|
-
* `{code, message}` populated from
|
|
107
|
-
*
|
|
123
|
+
* `{code, message}` populated from the selected dispatcher's
|
|
124
|
+
* per-target error decoration (`dispatchSequential` or
|
|
125
|
+
* `dispatchParallel` — same shape per the R-NEW-28 axis-1
|
|
126
|
+
* equivalence).
|
|
108
127
|
*
|
|
109
|
-
* `z.discriminatedUnion` would be the natural shape but
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
128
|
+
* `z.discriminatedUnion` would be the natural shape but the
|
|
129
|
+
* dispatchers' result records carry a dynamic id-field key
|
|
130
|
+
* (`{item_id: ..., ok, error?}`) — modelling that as a
|
|
131
|
+
* per-record union complicates the schema and downstream
|
|
113
132
|
* consumers' type-narrowing. The flatter shape below carries
|
|
114
133
|
* `item` + `error` as optionals; the action body's projection
|
|
115
134
|
* + the wrapper's per-item dispatch enforce the
|
|
@@ -234,7 +253,9 @@ export type PartialSuccessBulkUpdateData = z.infer<typeof partialSuccessBulkUpda
|
|
|
234
253
|
* applies — a stale-cache `validation_failed` remaps to the
|
|
235
254
|
* stable `column_archived` code agents key off (cli-design
|
|
236
255
|
* §6.5). The wrapper's per-item dispatch callback fires
|
|
237
|
-
* `foldAndRemap` BEFORE throwing into
|
|
256
|
+
* `foldAndRemap` BEFORE throwing into the selected dispatcher
|
|
257
|
+
* (`dispatchSequential` or `dispatchParallel` — both apply
|
|
258
|
+
* the same per-target error decoration) so
|
|
238
259
|
* the per-record `error.code` in `data.results[]` matches the
|
|
239
260
|
* shape the fail-fast path would have surfaced as the
|
|
240
261
|
* top-level `error.code`. That requires the same context the
|
|
@@ -283,15 +304,39 @@ export interface RunPartialSuccessBulkUpdateInputs {
|
|
|
283
304
|
readonly env: NodeJS.ProcessEnv;
|
|
284
305
|
readonly noCache: boolean;
|
|
285
306
|
readonly resolutionSource: 'live' | 'cache' | 'mixed';
|
|
307
|
+
/**
|
|
308
|
+
* v0.4-M30 `--concurrency <N>` argv slot (cli-design §9.3 +
|
|
309
|
+
* §6.4 "Bulk per-item partial-success — Parallel dispatch").
|
|
310
|
+
* `undefined` or `1` routes through `dispatchSequential`
|
|
311
|
+
* (byte-equivalent to the v0.3-M25 path); `> 1` routes
|
|
312
|
+
* through {@link dispatchParallel} (bounded async-pool).
|
|
313
|
+
* Action layer's argv parser pins the value to
|
|
314
|
+
* `[MIN_CONCURRENCY, MAX_CONCURRENCY]` (1..32) before reaching
|
|
315
|
+
* this helper.
|
|
316
|
+
*/
|
|
317
|
+
readonly concurrency: number | undefined;
|
|
318
|
+
/**
|
|
319
|
+
* v0.4-M30 SIGINT / abort threading. When the runner aborts
|
|
320
|
+
* `ctx.signal`, both dispatchers check `signal.aborted` at the
|
|
321
|
+
* iteration / worker-loop boundary and re-throw the signal's
|
|
322
|
+
* reason whole-call (mirrors {@link dispatchParallel} axis-6).
|
|
323
|
+
* In-flight wire calls abort via the existing
|
|
324
|
+
* `MondayClient.signal` configured at construction time
|
|
325
|
+
* (the client threads its signal into every fetch). Optional
|
|
326
|
+
* — omitting it preserves v0.3-M25 behaviour exactly for
|
|
327
|
+
* callers that don't need cooperative abort.
|
|
328
|
+
*/
|
|
329
|
+
readonly signal?: AbortSignal;
|
|
286
330
|
}
|
|
287
331
|
/**
|
|
288
332
|
* Result returned by {@link runPartialSuccessBulkUpdate} to the
|
|
289
333
|
* action layer.
|
|
290
334
|
*
|
|
291
335
|
* - `results` — the array of per-item records the helper built
|
|
292
|
-
* via `dispatchSequential`
|
|
293
|
-
*
|
|
294
|
-
*
|
|
336
|
+
* via the selected dispatcher (`dispatchSequential` or
|
|
337
|
+
* `dispatchParallel`) + the per-item projection callback.
|
|
338
|
+
* Direct mirror of `data.results[]` in the §6.4 envelope.
|
|
339
|
+
* **Mutable array** so the action layer can pass
|
|
295
340
|
* it directly to `partialSuccessBulkUpdateDataSchema.parse`
|
|
296
341
|
* (zod's `z.array(...)` infers a mutable array — wrapping
|
|
297
342
|
* `readonly` would force a spread at the call site).
|
|
@@ -326,19 +371,24 @@ export declare const PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE: EnvelopeSource;
|
|
|
326
371
|
/**
|
|
327
372
|
* Drives the per-item dispatch loop under `--continue-on-error`.
|
|
328
373
|
*
|
|
329
|
-
* Implementation (M25 impl `78889df` refactor + this commit
|
|
374
|
+
* Implementation (M25 impl `78889df` refactor + this commit;
|
|
375
|
+
* extended at v0.4-M30 pre-flight with the `concurrency` routing
|
|
376
|
+
* branch):
|
|
330
377
|
*
|
|
331
|
-
* 1. Loop {@link dispatchSequential}
|
|
332
|
-
*
|
|
378
|
+
* 1. Loop {@link dispatchSequential} (default / M25 path) OR
|
|
379
|
+
* {@link dispatchParallel} (v0.4-M30 `--concurrency > 1`
|
|
380
|
+
* path; runtime body landed at M30 IMPL) over
|
|
381
|
+
* `matchedItemIds` with id-field `'item_id'`.
|
|
333
382
|
* 2. Per-item dispatch callback fires
|
|
334
383
|
* {@link executeItemMutation} against the resolved
|
|
335
384
|
* `SelectedMutation`. On a {@link MondayCliError} catch,
|
|
336
385
|
* run {@link foldAndRemap} with `resolverWarnings` +
|
|
337
386
|
* `remapColumnIds` + `env` + `noCache` + `resolutionSource`
|
|
338
|
-
* from the inputs BEFORE re-throwing into
|
|
339
|
-
* `dispatchSequential
|
|
340
|
-
* `error.code` in
|
|
341
|
-
*
|
|
387
|
+
* from the inputs BEFORE re-throwing into the selected
|
|
388
|
+
* dispatcher (`dispatchSequential` or `dispatchParallel`).
|
|
389
|
+
* This makes the per-record `error.code` in
|
|
390
|
+
* `data.results[]` carry the SAME stable code
|
|
391
|
+
* (`column_archived` after a stale-cache
|
|
342
392
|
* `validation_failed` remap) that the v0.1 fail-fast
|
|
343
393
|
* path would have surfaced at the top level — Codex
|
|
344
394
|
* round-1 P1-1 contract requirement (cli-design §6.5
|
|
@@ -346,52 +396,60 @@ export declare const PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE: EnvelopeSource;
|
|
|
346
396
|
* fail-modes).
|
|
347
397
|
* 3. On success, capture the `ProjectedItem` into a side
|
|
348
398
|
* map keyed by `item_id`.
|
|
349
|
-
* 4. After the loop, walk the
|
|
350
|
-
*
|
|
399
|
+
* 4. After the loop, walk the result rows (from whichever
|
|
400
|
+
* dispatcher fired — `dispatchSequential` by default;
|
|
401
|
+
* {@link dispatchParallel} when `concurrency > 1`) and
|
|
402
|
+
* fold the per-item `ProjectedItem` from the side
|
|
351
403
|
* map into each `results[i].item` slot via
|
|
352
404
|
* {@link foldPartialSuccessBulkResult}. Failure records
|
|
353
405
|
* already carry `error: {code, message}` (with the
|
|
354
|
-
* foldAndRemap-applied code) via
|
|
355
|
-
*
|
|
406
|
+
* foldAndRemap-applied code) via the dispatcher's built-in
|
|
407
|
+
* error decoration (both routes share the same per-target
|
|
408
|
+
* error capture contract).
|
|
356
409
|
* 5. Return `{results}` — the action layer folds the
|
|
357
410
|
* constant `'live'` dispatch source via
|
|
358
411
|
* `sourceAgg.record(PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE,
|
|
359
412
|
* null)` and emits the envelope.
|
|
360
413
|
*
|
|
361
414
|
* **`internal_error` re-throw escape hatch.** Per M14 round-2
|
|
362
|
-
* F1 / round-3 F1,
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* `
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* the
|
|
415
|
+
* F1 / round-3 F1, both dispatchers re-throw `internal_error`
|
|
416
|
+
* whole-call so schema-drift in the response surfaces as
|
|
417
|
+
* top-level `ok: false` rather than per-record — papering over
|
|
418
|
+
* `internal_error` would hide the malformed-response signal
|
|
419
|
+
* agents need to know about. The wrapper inherits this
|
|
420
|
+
* behaviour by NOT wrapping the dispatcher's re-throw —
|
|
421
|
+
* `foldAndRemap` only ever runs against {@link MondayCliError}
|
|
422
|
+
* instances, and it NEVER converts a non-internal_error into
|
|
423
|
+
* internal_error, so the re-throw path through the selected
|
|
424
|
+
* dispatcher remains the canonical schema-drift surface (axis
|
|
425
|
+
* 2 of the R-NEW-28 6-axis equivalence — identical between
|
|
426
|
+
* `dispatchSequential` and `dispatchParallel`).
|
|
373
427
|
*
|
|
374
428
|
* **Non-`MondayCliError` re-throw.** Programmer-bug exceptions
|
|
375
429
|
* (TypeError, RangeError, etc.) raised by the executor or by
|
|
376
|
-
* `foldAndRemap`'s refresh probe propagate through
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* `
|
|
381
|
-
*
|
|
430
|
+
* `foldAndRemap`'s refresh probe propagate through the selected
|
|
431
|
+
* dispatcher's non-CliError re-throw branch unchanged, surfacing
|
|
432
|
+
* as whole-call `internal_error` via the runner's catch-all
|
|
433
|
+
* (mirrors M14's pattern at `users-fan-out-mutation.ts` and the
|
|
434
|
+
* documented behaviour at `partial-success-mutation.ts` —
|
|
435
|
+
* R-NEW-28 axis 3, also identical across both routes).
|
|
382
436
|
*/
|
|
383
437
|
export declare const runPartialSuccessBulkUpdate: (inputs: RunPartialSuccessBulkUpdateInputs) => Promise<RunPartialSuccessBulkUpdateResult>;
|
|
384
438
|
/**
|
|
385
|
-
* Pure helper — folds a
|
|
386
|
-
* `
|
|
387
|
-
*
|
|
439
|
+
* Pure helper — folds a per-target result row produced by the
|
|
440
|
+
* selected dispatcher (`dispatchSequential` or
|
|
441
|
+
* `dispatchParallel`) + a `ProjectedItem` side-map entry into
|
|
442
|
+
* the partial-success-bulk per-item record shape this module
|
|
443
|
+
* emits to the action layer. Both dispatchers populate the row
|
|
444
|
+
* with the same `{item_id, ok, error?}` shape (axis 1 of the
|
|
445
|
+
* R-NEW-28 6-axis equivalence) so the fold is route-agnostic.
|
|
388
446
|
*
|
|
389
447
|
* The helper is **shipped as a real implementation** (not a
|
|
390
448
|
* stub) so the pre-flight Codex review can verify the
|
|
391
449
|
* projection shape against the contract pinned in cli-design
|
|
392
450
|
* §6.4 inline. M25 implementation reuses the helper unchanged.
|
|
393
451
|
*
|
|
394
|
-
* `record` is the row produced by
|
|
452
|
+
* `record` is the row produced by the selected dispatcher with
|
|
395
453
|
* id-field `'item_id'` — carries `{item_id, ok, error?}` per
|
|
396
454
|
* the partial-success contract. `projectedItem` is the
|
|
397
455
|
* `ProjectedItem` the per-item dispatch callback captured on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partial-success-bulk.d.ts","sourceRoot":"","sources":["../../src/api/partial-success-bulk.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"partial-success-bulk.d.ts","sourceRoot":"","sources":["../../src/api/partial-success-bulk.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
* `item update --continue-on-error` flag (`cli-design.md` §6.4
|
|
4
4
|
* "Bulk per-item partial-success" sub-section).
|
|
5
5
|
*
|
|
6
|
-
* **What this module owns.** A thin wrapper around
|
|
7
|
-
* {@link dispatchSequential} from
|
|
8
|
-
* `src/api/partial-success-mutation.ts`
|
|
6
|
+
* **What this module owns.** A thin wrapper around the selected
|
|
7
|
+
* per-target dispatcher — {@link dispatchSequential} from
|
|
8
|
+
* `src/api/partial-success-mutation.ts` (default / M25 path) OR
|
|
9
|
+
* {@link dispatchParallel} from `src/api/parallel-dispatch.ts`
|
|
10
|
+
* (v0.4-M30 `--concurrency > 1` path) — that drives the matched-
|
|
9
11
|
* item-ID list through one wire call per item, capturing per-
|
|
10
12
|
* item failures into the result records rather than aborting
|
|
11
13
|
* the loop. The wrapper sits BETWEEN the bulk command-action
|
|
12
14
|
* orchestrator (`src/commands/item/update.ts:runBulk`) and the
|
|
13
|
-
* shared
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* shared dispatcher — the action body owns the matched-item-
|
|
16
|
+
* walk + column-resolution pre-pass + confirmation gate, then
|
|
17
|
+
* hands the resolved `SelectedMutation` + matched-item IDs to
|
|
18
|
+
* this wrapper, which fans them out + projects the partial-
|
|
19
|
+
* success envelope's `data.results[]` records.
|
|
18
20
|
*
|
|
19
21
|
* **Why a separate module rather than folding into update.ts.**
|
|
20
22
|
* Three reasons:
|
|
@@ -65,16 +67,31 @@
|
|
|
65
67
|
* 3-consumer trigger: single-item + fail-fast bulk + M25
|
|
66
68
|
* partial-success bulk).
|
|
67
69
|
*
|
|
68
|
-
* **
|
|
69
|
-
* {@link
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
70
|
+
* **v0.4-M30 extension.** Adds the `concurrency` input slot +
|
|
71
|
+
* the routing branch to {@link dispatchParallel} (new module
|
|
72
|
+
* `src/api/parallel-dispatch.ts` — runtime body landed at M30
|
|
73
|
+
* IMPL). When the caller passes `concurrency > 1`, the runtime
|
|
74
|
+
* fans out per-target dispatches via a bounded async-pool;
|
|
75
|
+
* absent or `concurrency === 1` preserves the M25 sequential
|
|
76
|
+
* path verbatim. The per-target dispatch closure is hoisted to
|
|
77
|
+
* a named local so both routes share the same
|
|
78
|
+
* `executeItemMutation` + `foldAndRemap` body — keeps the
|
|
79
|
+
* R-NEW-28 6-axis behavioral-equivalence audit straightforward.
|
|
80
|
+
* The M30 IMPL also threads an optional `signal?: AbortSignal`
|
|
81
|
+
* through both dispatchers (axis-6 scheduler short-circuit).
|
|
82
|
+
*
|
|
83
|
+
* **Per-item dispatch wiring.** Runtime body routes between
|
|
84
|
+
* {@link dispatchSequential} (default — `concurrency` absent /
|
|
85
|
+
* `=== 1`) and {@link dispatchParallel} (v0.4-M30 —
|
|
86
|
+
* `concurrency > 1`) over `matchedItemIds` with id-field
|
|
87
|
+
* `'item_id'`. The per-target dispatch callback (shared between
|
|
88
|
+
* routes verbatim) fires one `executeItemMutation` call.
|
|
89
|
+
* Successes populate `results[i].item` with the `ProjectedItem`
|
|
90
|
+
* via a side-map fold; failures land in
|
|
91
|
+
* `results[i].error: {code, message}` via the dispatcher's
|
|
92
|
+
* built-in error decoration. `internal_error` codes re-throw
|
|
93
|
+
* as whole-call (M14 round-2 F1 precedent — schema-drift in
|
|
94
|
+
* the response MUST NOT be papered over as a per-item failure).
|
|
78
95
|
*
|
|
79
96
|
* **`data.summary.failed_count` invariant.** The action body
|
|
80
97
|
* derives `failed_count` from the result records
|
|
@@ -90,6 +107,7 @@ import { z } from 'zod';
|
|
|
90
107
|
import { ApiError, MondayCliError } from '../utils/errors.js';
|
|
91
108
|
import { projectedItemSchema } from './item-projection.js';
|
|
92
109
|
import { dispatchSequential, } from './partial-success-mutation.js';
|
|
110
|
+
import { dispatchParallel } from './parallel-dispatch.js';
|
|
93
111
|
import { executeItemMutation } from './item-mutation-execute.js';
|
|
94
112
|
import { foldAndRemap } from './resolver-error-fold.js';
|
|
95
113
|
/**
|
|
@@ -102,13 +120,15 @@ import { foldAndRemap } from './resolver-error-fold.js';
|
|
|
102
120
|
* The `item` slot on success records is the §6.2 `ProjectedItem`
|
|
103
121
|
* shape (same projection single-item `item update` emits as
|
|
104
122
|
* `data`). The `error` slot on failure records carries
|
|
105
|
-
* `{code, message}` populated from
|
|
106
|
-
*
|
|
123
|
+
* `{code, message}` populated from the selected dispatcher's
|
|
124
|
+
* per-target error decoration (`dispatchSequential` or
|
|
125
|
+
* `dispatchParallel` — same shape per the R-NEW-28 axis-1
|
|
126
|
+
* equivalence).
|
|
107
127
|
*
|
|
108
|
-
* `z.discriminatedUnion` would be the natural shape but
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
128
|
+
* `z.discriminatedUnion` would be the natural shape but the
|
|
129
|
+
* dispatchers' result records carry a dynamic id-field key
|
|
130
|
+
* (`{item_id: ..., ok, error?}`) — modelling that as a
|
|
131
|
+
* per-record union complicates the schema and downstream
|
|
112
132
|
* consumers' type-narrowing. The flatter shape below carries
|
|
113
133
|
* `item` + `error` as optionals; the action body's projection
|
|
114
134
|
* + the wrapper's per-item dispatch enforce the
|
|
@@ -160,19 +180,24 @@ export const PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE = 'live';
|
|
|
160
180
|
/**
|
|
161
181
|
* Drives the per-item dispatch loop under `--continue-on-error`.
|
|
162
182
|
*
|
|
163
|
-
* Implementation (M25 impl `78889df` refactor + this commit
|
|
183
|
+
* Implementation (M25 impl `78889df` refactor + this commit;
|
|
184
|
+
* extended at v0.4-M30 pre-flight with the `concurrency` routing
|
|
185
|
+
* branch):
|
|
164
186
|
*
|
|
165
|
-
* 1. Loop {@link dispatchSequential}
|
|
166
|
-
*
|
|
187
|
+
* 1. Loop {@link dispatchSequential} (default / M25 path) OR
|
|
188
|
+
* {@link dispatchParallel} (v0.4-M30 `--concurrency > 1`
|
|
189
|
+
* path; runtime body landed at M30 IMPL) over
|
|
190
|
+
* `matchedItemIds` with id-field `'item_id'`.
|
|
167
191
|
* 2. Per-item dispatch callback fires
|
|
168
192
|
* {@link executeItemMutation} against the resolved
|
|
169
193
|
* `SelectedMutation`. On a {@link MondayCliError} catch,
|
|
170
194
|
* run {@link foldAndRemap} with `resolverWarnings` +
|
|
171
195
|
* `remapColumnIds` + `env` + `noCache` + `resolutionSource`
|
|
172
|
-
* from the inputs BEFORE re-throwing into
|
|
173
|
-
* `dispatchSequential
|
|
174
|
-
* `error.code` in
|
|
175
|
-
*
|
|
196
|
+
* from the inputs BEFORE re-throwing into the selected
|
|
197
|
+
* dispatcher (`dispatchSequential` or `dispatchParallel`).
|
|
198
|
+
* This makes the per-record `error.code` in
|
|
199
|
+
* `data.results[]` carry the SAME stable code
|
|
200
|
+
* (`column_archived` after a stale-cache
|
|
176
201
|
* `validation_failed` remap) that the v0.1 fail-fast
|
|
177
202
|
* path would have surfaced at the top level — Codex
|
|
178
203
|
* round-1 P1-1 contract requirement (cli-design §6.5
|
|
@@ -180,44 +205,57 @@ export const PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE = 'live';
|
|
|
180
205
|
* fail-modes).
|
|
181
206
|
* 3. On success, capture the `ProjectedItem` into a side
|
|
182
207
|
* map keyed by `item_id`.
|
|
183
|
-
* 4. After the loop, walk the
|
|
184
|
-
*
|
|
208
|
+
* 4. After the loop, walk the result rows (from whichever
|
|
209
|
+
* dispatcher fired — `dispatchSequential` by default;
|
|
210
|
+
* {@link dispatchParallel} when `concurrency > 1`) and
|
|
211
|
+
* fold the per-item `ProjectedItem` from the side
|
|
185
212
|
* map into each `results[i].item` slot via
|
|
186
213
|
* {@link foldPartialSuccessBulkResult}. Failure records
|
|
187
214
|
* already carry `error: {code, message}` (with the
|
|
188
|
-
* foldAndRemap-applied code) via
|
|
189
|
-
*
|
|
215
|
+
* foldAndRemap-applied code) via the dispatcher's built-in
|
|
216
|
+
* error decoration (both routes share the same per-target
|
|
217
|
+
* error capture contract).
|
|
190
218
|
* 5. Return `{results}` — the action layer folds the
|
|
191
219
|
* constant `'live'` dispatch source via
|
|
192
220
|
* `sourceAgg.record(PARTIAL_SUCCESS_BULK_DISPATCH_SOURCE,
|
|
193
221
|
* null)` and emits the envelope.
|
|
194
222
|
*
|
|
195
223
|
* **`internal_error` re-throw escape hatch.** Per M14 round-2
|
|
196
|
-
* F1 / round-3 F1,
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* `
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* the
|
|
224
|
+
* F1 / round-3 F1, both dispatchers re-throw `internal_error`
|
|
225
|
+
* whole-call so schema-drift in the response surfaces as
|
|
226
|
+
* top-level `ok: false` rather than per-record — papering over
|
|
227
|
+
* `internal_error` would hide the malformed-response signal
|
|
228
|
+
* agents need to know about. The wrapper inherits this
|
|
229
|
+
* behaviour by NOT wrapping the dispatcher's re-throw —
|
|
230
|
+
* `foldAndRemap` only ever runs against {@link MondayCliError}
|
|
231
|
+
* instances, and it NEVER converts a non-internal_error into
|
|
232
|
+
* internal_error, so the re-throw path through the selected
|
|
233
|
+
* dispatcher remains the canonical schema-drift surface (axis
|
|
234
|
+
* 2 of the R-NEW-28 6-axis equivalence — identical between
|
|
235
|
+
* `dispatchSequential` and `dispatchParallel`).
|
|
207
236
|
*
|
|
208
237
|
* **Non-`MondayCliError` re-throw.** Programmer-bug exceptions
|
|
209
238
|
* (TypeError, RangeError, etc.) raised by the executor or by
|
|
210
|
-
* `foldAndRemap`'s refresh probe propagate through
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* `
|
|
215
|
-
*
|
|
239
|
+
* `foldAndRemap`'s refresh probe propagate through the selected
|
|
240
|
+
* dispatcher's non-CliError re-throw branch unchanged, surfacing
|
|
241
|
+
* as whole-call `internal_error` via the runner's catch-all
|
|
242
|
+
* (mirrors M14's pattern at `users-fan-out-mutation.ts` and the
|
|
243
|
+
* documented behaviour at `partial-success-mutation.ts` —
|
|
244
|
+
* R-NEW-28 axis 3, also identical across both routes).
|
|
216
245
|
*/
|
|
217
246
|
export const runPartialSuccessBulkUpdate = async (inputs) => {
|
|
218
|
-
const { client, boardId, matchedItemIds, mutation, createLabelsIfMissing, resolverWarnings, remapColumnIds, env, noCache, resolutionSource, } = inputs;
|
|
247
|
+
const { client, boardId, matchedItemIds, mutation, createLabelsIfMissing, resolverWarnings, remapColumnIds, env, noCache, resolutionSource, concurrency, signal, } = inputs;
|
|
219
248
|
const projectedById = new Map();
|
|
220
|
-
|
|
249
|
+
// Per-target dispatch closure shared between the sequential
|
|
250
|
+
// (v0.3-M25 default) and parallel (v0.4-M30 `--concurrency > 1`)
|
|
251
|
+
// routes. Both dispatch helpers contract on the same
|
|
252
|
+
// {@link DispatchOneTargetInputs}-shaped callback so the closure
|
|
253
|
+
// body is byte-equivalent across routes — only the OUTER call
|
|
254
|
+
// (dispatchSequential vs dispatchParallel) changes. This keeps the
|
|
255
|
+
// R-NEW-28 6-axis behavioral-equivalence audit straightforward:
|
|
256
|
+
// every per-target outcome (success projection capture; `MondayCliError`
|
|
257
|
+
// foldAndRemap + re-throw; non-CliError re-throw) is shared verbatim.
|
|
258
|
+
const perTargetDispatch = async ({ targetId }) => {
|
|
221
259
|
try {
|
|
222
260
|
const result = await executeItemMutation(client, {
|
|
223
261
|
mutation,
|
|
@@ -238,9 +276,9 @@ export const runPartialSuccessBulkUpdate = async (inputs) => {
|
|
|
238
276
|
// even though the v0.1 path surfaces `column_archived`
|
|
239
277
|
// for the same root cause (cli-design §6.5 stable-
|
|
240
278
|
// code rule). foldAndRemap NEVER converts a non-
|
|
241
|
-
// internal_error into internal_error, so
|
|
242
|
-
//
|
|
243
|
-
//
|
|
279
|
+
// internal_error into internal_error, so the selected
|
|
280
|
+
// dispatcher's internal_error re-throw escape hatch
|
|
281
|
+
// (M14 round-2 F1) stays intact across both routes.
|
|
244
282
|
const remapped = await foldAndRemap({
|
|
245
283
|
err,
|
|
246
284
|
warnings: resolverWarnings,
|
|
@@ -254,18 +292,33 @@ export const runPartialSuccessBulkUpdate = async (inputs) => {
|
|
|
254
292
|
throw remapped;
|
|
255
293
|
}
|
|
256
294
|
// Non-MondayCliError — programmer bug. Re-throw through
|
|
257
|
-
// dispatchSequential's non-CliError branch
|
|
258
|
-
// catch-all surfaces it as internal_error
|
|
259
|
-
// not per-record). Mirrors users-fan-out-mutation.ts
|
|
260
|
-
// and is the documented
|
|
295
|
+
// dispatchSequential / dispatchParallel's non-CliError branch
|
|
296
|
+
// so the runner's catch-all surfaces it as internal_error
|
|
297
|
+
// (whole-call, not per-record). Mirrors users-fan-out-mutation.ts
|
|
298
|
+
// and is the documented partial-success contract.
|
|
261
299
|
throw err;
|
|
262
300
|
}
|
|
263
|
-
}
|
|
301
|
+
};
|
|
302
|
+
// v0.4-M30 routing: `--concurrency <N>` with N > 1 routes through
|
|
303
|
+
// {@link dispatchParallel} (bounded async-pool); absent / N === 1
|
|
304
|
+
// routes through the unchanged {@link dispatchSequential} path.
|
|
305
|
+
// Both dispatchers thread the optional `signal` so SIGINT-aware
|
|
306
|
+
// callers see consistent cooperative abort semantics across routes
|
|
307
|
+
// (R-NEW-28 axis 6).
|
|
308
|
+
let dispatchResults;
|
|
309
|
+
if (concurrency !== undefined && concurrency > 1) {
|
|
310
|
+
dispatchResults = await dispatchParallel(matchedItemIds, 'item_id', perTargetDispatch, concurrency, signal);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
dispatchResults = await dispatchSequential(matchedItemIds, 'item_id', perTargetDispatch, signal);
|
|
314
|
+
}
|
|
264
315
|
const results = dispatchResults.map((row) => {
|
|
265
316
|
// Side-map lookup requires the item_id string from the row;
|
|
266
317
|
// foldPartialSuccessBulkResult also enforces the same
|
|
267
318
|
// invariant + throws internal_error if the id-field is
|
|
268
|
-
// missing or non-string (
|
|
319
|
+
// missing or non-string (both dispatchers populate the
|
|
320
|
+
// id-field slot for every result row per the partial-
|
|
321
|
+
// success contract).
|
|
269
322
|
const itemIdSlot = row.item_id;
|
|
270
323
|
const projected = typeof itemIdSlot === 'string'
|
|
271
324
|
? projectedById.get(itemIdSlot)
|
|
@@ -275,16 +328,20 @@ export const runPartialSuccessBulkUpdate = async (inputs) => {
|
|
|
275
328
|
return { results };
|
|
276
329
|
};
|
|
277
330
|
/**
|
|
278
|
-
* Pure helper — folds a
|
|
279
|
-
* `
|
|
280
|
-
*
|
|
331
|
+
* Pure helper — folds a per-target result row produced by the
|
|
332
|
+
* selected dispatcher (`dispatchSequential` or
|
|
333
|
+
* `dispatchParallel`) + a `ProjectedItem` side-map entry into
|
|
334
|
+
* the partial-success-bulk per-item record shape this module
|
|
335
|
+
* emits to the action layer. Both dispatchers populate the row
|
|
336
|
+
* with the same `{item_id, ok, error?}` shape (axis 1 of the
|
|
337
|
+
* R-NEW-28 6-axis equivalence) so the fold is route-agnostic.
|
|
281
338
|
*
|
|
282
339
|
* The helper is **shipped as a real implementation** (not a
|
|
283
340
|
* stub) so the pre-flight Codex review can verify the
|
|
284
341
|
* projection shape against the contract pinned in cli-design
|
|
285
342
|
* §6.4 inline. M25 implementation reuses the helper unchanged.
|
|
286
343
|
*
|
|
287
|
-
* `record` is the row produced by
|
|
344
|
+
* `record` is the row produced by the selected dispatcher with
|
|
288
345
|
* id-field `'item_id'` — carries `{item_id, ok, error?}` per
|
|
289
346
|
* the partial-success contract. `projectedItem` is the
|
|
290
347
|
* `ProjectedItem` the per-item dispatch callback captured on
|
|
@@ -297,16 +354,17 @@ export const runPartialSuccessBulkUpdate = async (inputs) => {
|
|
|
297
354
|
* records never carry `item`.
|
|
298
355
|
*/
|
|
299
356
|
export const foldPartialSuccessBulkResult = (record, projectedItem) => {
|
|
300
|
-
// Dot-access: `dispatchSequential`
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
//
|
|
357
|
+
// Dot-access: the selected dispatcher (`dispatchSequential` or
|
|
358
|
+
// `dispatchParallel`) builds the record with the dynamic
|
|
359
|
+
// id-field key (`'item_id'`) carrying the target ID. The
|
|
360
|
+
// dot-access narrows the unknown index-signature value to a
|
|
361
|
+
// string via the runtime guard below; the helper throws
|
|
362
|
+
// `internal_error` if the shape doesn't match (which would be
|
|
363
|
+
// a programmer bug — both dispatchers contract on populating
|
|
364
|
+
// the id-field slot for every result row).
|
|
307
365
|
const itemIdSlot = record.item_id;
|
|
308
366
|
if (typeof itemIdSlot !== 'string' || itemIdSlot.length === 0) {
|
|
309
|
-
throw new ApiError('internal_error', 'partial-success bulk result row is missing the `item_id` field —
|
|
367
|
+
throw new ApiError('internal_error', 'partial-success bulk result row is missing the `item_id` field — dispatcher contract violation.', {
|
|
310
368
|
details: {
|
|
311
369
|
record_keys: Object.keys(record),
|
|
312
370
|
},
|
|
@@ -326,11 +384,11 @@ export const foldPartialSuccessBulkResult = (record, projectedItem) => {
|
|
|
326
384
|
item: projectedItem,
|
|
327
385
|
};
|
|
328
386
|
}
|
|
329
|
-
// Failure path —
|
|
330
|
-
//
|
|
331
|
-
// narrow defensively here.
|
|
387
|
+
// Failure path — both dispatchers populate `error` on every
|
|
388
|
+
// non-`ok` row (R-NEW-28 axis 1); the schema's `.optional()`
|
|
389
|
+
// declarations narrow defensively here.
|
|
332
390
|
if (record.error === undefined) {
|
|
333
|
-
throw new ApiError('internal_error', `partial-success bulk result row for item_id ${itemIdSlot} reported ok: false but no error payload was captured —
|
|
391
|
+
throw new ApiError('internal_error', `partial-success bulk result row for item_id ${itemIdSlot} reported ok: false but no error payload was captured — dispatcher contract violation.`, {
|
|
334
392
|
details: {
|
|
335
393
|
item_id: itemIdSlot,
|
|
336
394
|
},
|