deepline 0.3.343 → 0.3.345

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +9 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +240 -66
  4. package/dist/bundling-sources/shared_libs/plays/csv-text-repair.ts +145 -0
  5. package/dist/cli/auth-4HCZZPZH.js +31 -0
  6. package/dist/cli/{auth-IPOSX2NV.mjs → auth-YQSQYS2V.mjs} +4 -4
  7. package/dist/cli/{billing-CWTZDAOC.mjs → billing-HA75G3SK.mjs} +4 -4
  8. package/dist/cli/{billing-ZZKX4IFS.js → billing-JRP2ZFMV.js} +71 -71
  9. package/dist/cli/{chunk-JGW7AR5N.js → chunk-2OTDDG2J.js} +2 -2
  10. package/dist/cli/{chunk-UJFBO6QV.mjs → chunk-3XKBPPP7.mjs} +2 -2
  11. package/dist/cli/{chunk-ELZ4HEPQ.js → chunk-4INBT3OR.js} +225 -225
  12. package/dist/cli/{chunk-CVIAYYLR.mjs → chunk-5QPHJFNN.mjs} +2 -2
  13. package/dist/cli/{chunk-RB2ELEQ5.mjs → chunk-AW2G44QA.mjs} +4 -4
  14. package/dist/cli/{chunk-ZQFYR6NT.mjs → chunk-DOPNLW2H.mjs} +6 -6
  15. package/dist/cli/{chunk-FM36CTMM.js → chunk-EEPFLZ7V.js} +9 -9
  16. package/dist/cli/{chunk-U5L6FIIL.mjs → chunk-EWYAS4WD.mjs} +1 -1
  17. package/dist/cli/{chunk-ERFIHZXT.js → chunk-FBUWH67Y.js} +48 -48
  18. package/dist/cli/{chunk-XOX3Y5OP.js → chunk-FDH572QX.js} +45 -45
  19. package/dist/cli/{chunk-LY33N2W6.mjs → chunk-GGRQBO3S.mjs} +2 -2
  20. package/dist/cli/{chunk-JS7JVUZZ.js → chunk-IT6MAYZ3.js} +8 -8
  21. package/dist/cli/{chunk-IPEE3DIV.js → chunk-ITOJRSOU.js} +112 -24
  22. package/dist/cli/{chunk-W6LB26RC.mjs → chunk-JDSRPSES.mjs} +1 -1
  23. package/dist/cli/{chunk-RDXH4KXG.js → chunk-KXFPDFUO.js} +26 -26
  24. package/dist/cli/{chunk-QV2WIEUP.mjs → chunk-MLO7ZGSH.mjs} +2 -2
  25. package/dist/cli/{chunk-AUB3AW3P.js → chunk-MO6QJPEB.js} +4 -4
  26. package/dist/cli/{chunk-WBXTRZAX.mjs → chunk-N3GUUSDO.mjs} +1 -1
  27. package/dist/cli/{chunk-2Y2QZJBT.mjs → chunk-NTIWTBEH.mjs} +94 -6
  28. package/dist/cli/{chunk-KLEZD6JX.mjs → chunk-S3JKJL44.mjs} +5 -5
  29. package/dist/cli/{chunk-DEDNTNXS.mjs → chunk-TAX3OOC6.mjs} +1 -1
  30. package/dist/cli/{chunk-ZGIXGBKF.js → chunk-TDW3RU7S.js} +15 -15
  31. package/dist/cli/{chunk-FSNKEEBA.js → chunk-UJCHR3J3.js} +1 -1
  32. package/dist/cli/{chunk-63UBRDGC.js → chunk-URPZWUVN.js} +60 -60
  33. package/dist/cli/{chunk-BCF43TLG.js → chunk-WRTLSH6R.js} +8 -8
  34. package/dist/cli/{chunk-GISYO3JA.mjs → chunk-YGQ4AIK7.mjs} +3 -3
  35. package/dist/cli/{client-7ZUYHDCL.mjs → client-VKQGBPZ5.mjs} +3 -3
  36. package/dist/cli/client-VOXK5Z4D.js +20 -0
  37. package/dist/cli/{csv-7BL43EOB.js → csv-UNP7KAXF.js} +12 -12
  38. package/dist/cli/{csv-6CFRU4CQ.mjs → csv-ZFMAI2HY.mjs} +3 -3
  39. package/dist/cli/{db-RUKRHPBV.mjs → db-C5YWXRLY.mjs} +4 -4
  40. package/dist/cli/{db-EJETRBBT.js → db-LDLXC2JQ.js} +29 -29
  41. package/dist/cli/{describe-N46DGDQ4.js → describe-FO62H2CB.js} +15 -15
  42. package/dist/cli/{describe-AVBCGKX4.mjs → describe-UYNKJV6T.mjs} +8 -8
  43. package/dist/cli/{executions-OPBBBSDT.js → executions-DIADSK2E.js} +6 -6
  44. package/dist/cli/{executions-FCTCQYY3.mjs → executions-JYKV3GQJ.mjs} +4 -4
  45. package/dist/cli/{feedback-XY7F7FF6.mjs → feedback-4UEJJFRL.mjs} +3 -3
  46. package/dist/cli/{feedback-RSPSGS2W.js → feedback-YBO47O7G.js} +7 -7
  47. package/dist/cli/{git-ZQ534TRG.mjs → git-A6DPLCMU.mjs} +4 -4
  48. package/dist/cli/git-FPC5VIIK.js +23 -0
  49. package/dist/cli/index.js +79 -79
  50. package/dist/cli/index.mjs +31 -31
  51. package/dist/cli/{legacy-noops-WCLAYQNO.js → legacy-noops-F6TJLZV2.js} +7 -7
  52. package/dist/cli/{legacy-noops-S4Q4TBKQ.mjs → legacy-noops-HCAOJI7X.mjs} +4 -4
  53. package/dist/cli/{monitors-UD6C7YUQ.js → monitors-B5QFSFK7.js} +85 -85
  54. package/dist/cli/{monitors-7EONZILI.mjs → monitors-XYP7XNCT.mjs} +4 -4
  55. package/dist/cli/{org-EGED4FVT.js → org-NMWNBQZI.js} +60 -60
  56. package/dist/cli/{org-SPZF2UBU.mjs → org-O5VBVLSP.mjs} +4 -4
  57. package/dist/cli/play-D2SCAJXG.js +153 -0
  58. package/dist/cli/{play-KXWJX7RB.mjs → play-Z2STHYQB.mjs} +7 -7
  59. package/dist/cli/{providers-HWGPSJZ7.js → providers-BAPO54GE.js} +7 -7
  60. package/dist/cli/{providers-P2MRBKX2.mjs → providers-WGIJLFBZ.mjs} +4 -4
  61. package/dist/cli/{quickstart-CL4AB46M.js → quickstart-JGRMJ6M4.js} +5 -5
  62. package/dist/cli/{quickstart-VEEHUU7O.mjs → quickstart-LL6KUMJ5.mjs} +3 -3
  63. package/dist/cli/{search-D7CLJLVU.mjs → search-P7BVN4OI.mjs} +5 -5
  64. package/dist/cli/search-UNJN5P72.js +26 -0
  65. package/dist/cli/{secrets-ZDW4MTJL.mjs → secrets-5JHLILQD.mjs} +4 -4
  66. package/dist/cli/{secrets-NGB5OD2W.js → secrets-QUBFPBE5.js} +13 -13
  67. package/dist/cli/{sessions-HYUYIAHB.mjs → sessions-3BYS3WQZ.mjs} +4 -4
  68. package/dist/cli/{sessions-MTN3SBTL.js → sessions-TATDTEY6.js} +5 -5
  69. package/dist/cli/{settings-X43X7H54.js → settings-SLFYXPRP.js} +74 -74
  70. package/dist/cli/{settings-RMABGZ6N.mjs → settings-TXWQLO34.mjs} +7 -7
  71. package/dist/cli/{setup-QEM6U5TO.js → setup-A3CXCYRI.js} +41 -41
  72. package/dist/cli/{setup-N7KUH3AM.mjs → setup-XTAW2JS2.mjs} +6 -6
  73. package/dist/cli/{skills-H3STANPK.mjs → skills-P323CMPH.mjs} +4 -4
  74. package/dist/cli/skills-ZOUUC72T.js +28 -0
  75. package/dist/cli/{tools-FSEOJNTY.mjs → tools-FMSH7Z7I.mjs} +6 -6
  76. package/dist/cli/tools-O7TLYHFT.js +40 -0
  77. package/dist/cli/update-3LT6YSIP.js +28 -0
  78. package/dist/cli/{update-6BUK4OLS.mjs → update-CGKRTVOD.mjs} +6 -6
  79. package/dist/cli/{workflow-Q7RJGAKL.mjs → workflow-2DVIYBLG.mjs} +5 -5
  80. package/dist/cli/{workflow-4GZCYDZS.js → workflow-EFOSJKW7.js} +15 -15
  81. package/dist/index.js +6 -6
  82. package/dist/index.mjs +3 -3
  83. package/dist/install-integrity.json +75 -74
  84. package/dist/release.d.mts +1 -1
  85. package/dist/release.d.ts +1 -1
  86. package/dist/release.js +2 -2
  87. package/dist/release.mjs +1 -1
  88. package/package.json +1 -1
  89. package/dist/cli/auth-J2C5CQ7M.js +0 -31
  90. package/dist/cli/client-CCDQC7ZY.js +0 -20
  91. package/dist/cli/git-DZTOBIUF.js +0 -23
  92. package/dist/cli/play-5MDIQGCF.js +0 -153
  93. package/dist/cli/search-6H77HQL3.js +0 -26
  94. package/dist/cli/skills-7H2OD626.js +0 -28
  95. package/dist/cli/tools-UYW5T5PU.js +0 -40
  96. package/dist/cli/update-K743Q4EF.js +0 -28
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
202
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
203
- version: '0.3.343',
203
+ version: '0.3.345',
204
204
  updateSummary:
205
205
  'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
206
206
  packageCapabilities: {
@@ -10,6 +10,7 @@ import { compileFunction } from 'node:vm';
10
10
  import { gunzipSync } from 'node:zlib';
11
11
  import { Readable } from 'node:stream';
12
12
  import { parse } from 'csv-parse';
13
+ import { csvInputTextOptions } from '../../plays/csv-text-repair';
13
14
  import { install as installSourceMapSupport } from 'source-map-support';
14
15
  import {
15
16
  createDeferredPlayDataset,
@@ -1559,7 +1560,14 @@ async function emitAndExit(
1559
1560
  function parseCsvStream(
1560
1561
  source: NodeJS.ReadableStream,
1561
1562
  ): AsyncIterable<Record<string, unknown>> {
1562
- const parser = parse({ columns: true, skip_empty_lines: true, trim: true });
1563
+ // Every Play's CSV input passes through here, so this is where a customer
1564
+ // file's BOM and mis-decoded UTF-8 ("j̩r̫me", "o’malley") get fixed before
1565
+ // any row reaches a provider. See ../../plays/csv-text-repair.
1566
+ const parser = parse({
1567
+ ...csvInputTextOptions(),
1568
+ skip_empty_lines: true,
1569
+ trim: true,
1570
+ });
1563
1571
  // `Readable.pipe()` does not forward source errors to the destination. A
1564
1572
  // missing staged file therefore used to emit an unhandled `error` from
1565
1573
  // createReadStream and kill the Daytona process outside main().catch().
@@ -520,6 +520,68 @@ export type RuntimeSheetTiming = {
520
520
  error?: string;
521
521
  };
522
522
 
523
+ type RuntimeMapCompletionPhase =
524
+ | 'dbSessionAcquire'
525
+ | 'poolAcquire'
526
+ | 'transactionBegin'
527
+ | 'mapRowsUpdate'
528
+ | 'missingRowsInsert'
529
+ | 'failedRowsUpdate'
530
+ | 'transactionCommit'
531
+ | 'queryClient';
532
+
533
+ type RuntimeMapCompletionDiagnostics = Record<
534
+ `${RuntimeMapCompletionPhase}Ms` | `${RuntimeMapCompletionPhase}Failures`,
535
+ number
536
+ > & {
537
+ operationAttempts: number;
538
+ provisioningRetryAttempts: number;
539
+ };
540
+
541
+ function createRuntimeMapCompletionDiagnostics(): RuntimeMapCompletionDiagnostics {
542
+ return {
543
+ dbSessionAcquireMs: 0,
544
+ dbSessionAcquireFailures: 0,
545
+ poolAcquireMs: 0,
546
+ poolAcquireFailures: 0,
547
+ transactionBeginMs: 0,
548
+ transactionBeginFailures: 0,
549
+ mapRowsUpdateMs: 0,
550
+ mapRowsUpdateFailures: 0,
551
+ missingRowsInsertMs: 0,
552
+ missingRowsInsertFailures: 0,
553
+ failedRowsUpdateMs: 0,
554
+ failedRowsUpdateFailures: 0,
555
+ transactionCommitMs: 0,
556
+ transactionCommitFailures: 0,
557
+ queryClientMs: 0,
558
+ queryClientFailures: 0,
559
+ operationAttempts: 0,
560
+ provisioningRetryAttempts: 0,
561
+ };
562
+ }
563
+
564
+ async function measureRuntimeSheetPhase<T>(
565
+ diagnostics: RuntimeMapCompletionDiagnostics | undefined,
566
+ phase: RuntimeMapCompletionPhase,
567
+ operation: () => Promise<T>,
568
+ ): Promise<T> {
569
+ if (!diagnostics) return await operation();
570
+ const startedAt = Date.now();
571
+ let failed = false;
572
+ try {
573
+ return await operation();
574
+ } catch (error) {
575
+ failed = true;
576
+ throw error;
577
+ } finally {
578
+ const msKey = `${phase}Ms` as const;
579
+ const failuresKey = `${phase}Failures` as const;
580
+ diagnostics[msKey] += Date.now() - startedAt;
581
+ diagnostics[failuresKey] += Number(failed);
582
+ }
583
+ }
584
+
523
585
  export type RuntimeSheetAttemptHeartbeatResult = {
524
586
  renewed: number;
525
587
  renewedKeys: string[];
@@ -2314,6 +2376,7 @@ async function withRuntimePostgres<T>(
2314
2376
  maxConnectAttempts?: number;
2315
2377
  operationTimeoutMs?: number;
2316
2378
  signal?: AbortSignal | null;
2379
+ completionDiagnostics?: RuntimeMapCompletionDiagnostics;
2317
2380
  } = {},
2318
2381
  ): Promise<T> {
2319
2382
  let client: RuntimePoolClient | null = null;
@@ -2336,24 +2399,35 @@ async function withRuntimePostgres<T>(
2336
2399
  if (!cachePool) {
2337
2400
  requestLocalPool = pool;
2338
2401
  }
2339
- client = await connectRuntimePostgresPool(
2340
- pool,
2341
- admission,
2342
- lane,
2343
- session.postgresUrl,
2344
- options.signal,
2402
+ client = await measureRuntimeSheetPhase(
2403
+ options.completionDiagnostics,
2404
+ 'poolAcquire',
2405
+ () =>
2406
+ connectRuntimePostgresPool(
2407
+ pool,
2408
+ admission,
2409
+ lane,
2410
+ session.postgresUrl,
2411
+ options.signal,
2412
+ ),
2345
2413
  );
2346
2414
  operationDeadlineAt =
2347
2415
  options.operationTimeoutMs === undefined
2348
2416
  ? null
2349
2417
  : Date.now() + Math.max(1, Math.floor(options.operationTimeoutMs));
2350
2418
  if (session.executionRole) {
2351
- await awaitRuntimePostgresOperation({
2352
- client,
2353
- deadlineAt: operationDeadlineAt,
2354
- operation: client.query('BEGIN'),
2355
- signal: options.signal,
2356
- });
2419
+ const roleClient = client;
2420
+ await measureRuntimeSheetPhase(
2421
+ options.completionDiagnostics,
2422
+ 'transactionBegin',
2423
+ () =>
2424
+ awaitRuntimePostgresOperation({
2425
+ client: roleClient,
2426
+ deadlineAt: operationDeadlineAt,
2427
+ operation: roleClient.query('BEGIN'),
2428
+ signal: options.signal,
2429
+ }),
2430
+ );
2357
2431
  roleTransactionStarted = true;
2358
2432
  await awaitRuntimePostgresOperation({
2359
2433
  client,
@@ -2419,12 +2493,18 @@ async function withRuntimePostgres<T>(
2419
2493
  signal: options.signal,
2420
2494
  });
2421
2495
  if (roleTransactionStarted) {
2422
- await awaitRuntimePostgresOperation({
2423
- client,
2424
- deadlineAt: operationDeadlineAt,
2425
- operation: client.query('COMMIT'),
2426
- signal: options.signal,
2427
- });
2496
+ const roleClient = client;
2497
+ await measureRuntimeSheetPhase(
2498
+ options.completionDiagnostics,
2499
+ 'transactionCommit',
2500
+ () =>
2501
+ awaitRuntimePostgresOperation({
2502
+ client: roleClient,
2503
+ deadlineAt: operationDeadlineAt,
2504
+ operation: roleClient.query('COMMIT'),
2505
+ signal: options.signal,
2506
+ }),
2507
+ );
2428
2508
  roleTransactionStarted = false;
2429
2509
  }
2430
2510
  return result;
@@ -2549,10 +2629,14 @@ async function withRuntimeSheetProvisioningRetry<T>(
2549
2629
  sheetContract: PlaySheetContract;
2550
2630
  session: RuntimePostgresSession;
2551
2631
  timings?: RuntimeSheetTiming[];
2632
+ completionDiagnostics?: RuntimeMapCompletionDiagnostics;
2552
2633
  },
2553
2634
  operation: () => Promise<T>,
2554
2635
  ): Promise<T> {
2555
2636
  const firstAttemptStartedAt = Date.now();
2637
+ if (input.completionDiagnostics) {
2638
+ input.completionDiagnostics.operationAttempts += 1;
2639
+ }
2556
2640
  try {
2557
2641
  const result = await operation();
2558
2642
  input.timings?.push({
@@ -2610,6 +2694,10 @@ async function withRuntimeSheetProvisioningRetry<T>(
2610
2694
  } else {
2611
2695
  throw error;
2612
2696
  }
2697
+ if (input.completionDiagnostics) {
2698
+ input.completionDiagnostics.provisioningRetryAttempts += 1;
2699
+ input.completionDiagnostics.operationAttempts += 1;
2700
+ }
2613
2701
  const retryStartedAt = Date.now();
2614
2702
  const result = await operation();
2615
2703
  input.timings?.push({
@@ -2631,11 +2719,12 @@ async function withRuntimeSheetQueryClient<T>(
2631
2719
  transactional: boolean;
2632
2720
  statementTimeoutMs?: number;
2633
2721
  timings?: RuntimeSheetTiming[];
2722
+ completionDiagnostics?: RuntimeMapCompletionDiagnostics;
2634
2723
  },
2635
2724
  operation: (client: RuntimeQueryClient) => Promise<T>,
2636
2725
  ): Promise<T> {
2637
- const totalStartedAt = Date.now();
2638
- const result = await withRuntimeSheetProvisioningRetry(
2726
+ const resultStartedAt = Date.now();
2727
+ const resultPromise = withRuntimeSheetProvisioningRetry(
2639
2728
  context,
2640
2729
  { ...input, session },
2641
2730
  async () => {
@@ -2661,7 +2750,13 @@ async function withRuntimeSheetQueryClient<T>(
2661
2750
  const managesTransaction =
2662
2751
  (input.transactional || input.statementTimeoutMs !== undefined) &&
2663
2752
  !transaction.active;
2664
- if (managesTransaction) await client.query('BEGIN');
2753
+ if (managesTransaction) {
2754
+ await measureRuntimeSheetPhase(
2755
+ input.completionDiagnostics,
2756
+ 'transactionBegin',
2757
+ () => client.query('BEGIN'),
2758
+ );
2759
+ }
2665
2760
  try {
2666
2761
  if (input.statementTimeoutMs !== undefined) {
2667
2762
  await client.query(
@@ -2669,7 +2764,13 @@ async function withRuntimeSheetQueryClient<T>(
2669
2764
  );
2670
2765
  }
2671
2766
  const result = await operation(client);
2672
- if (managesTransaction) await client.query('COMMIT');
2767
+ if (managesTransaction) {
2768
+ await measureRuntimeSheetPhase(
2769
+ input.completionDiagnostics,
2770
+ 'transactionCommit',
2771
+ () => client.query('COMMIT'),
2772
+ );
2773
+ }
2673
2774
  return result;
2674
2775
  } catch (error) {
2675
2776
  if (managesTransaction) {
@@ -2687,13 +2788,22 @@ async function withRuntimeSheetQueryClient<T>(
2687
2788
  ? undefined
2688
2789
  : RUNTIME_SHEET_COMPLETION_SERVER_OPERATION_TIMEOUT_MS,
2689
2790
  signal: context.abortSignal,
2791
+ completionDiagnostics: input.completionDiagnostics,
2690
2792
  },
2691
2793
  );
2692
2794
  },
2693
2795
  );
2796
+ if (input.completionDiagnostics) {
2797
+ return await measureRuntimeSheetPhase(
2798
+ input.completionDiagnostics,
2799
+ 'queryClient',
2800
+ () => resultPromise,
2801
+ );
2802
+ }
2803
+ const result = await resultPromise;
2694
2804
  input.timings?.push({
2695
2805
  phase: 'query_client_total',
2696
- ms: Date.now() - totalStartedAt,
2806
+ ms: Date.now() - resultStartedAt,
2697
2807
  });
2698
2808
  return result;
2699
2809
  }
@@ -8686,24 +8796,62 @@ export async function completeRuntimeMapRows(
8686
8796
  rows: input.rows.map((row) => row.data),
8687
8797
  outputFields: input.outputFields,
8688
8798
  });
8689
- const session = requireRuntimePostgresSession(
8690
- await getRuntimeDbSession(
8691
- {
8692
- ...context,
8693
- playName: context.playName,
8694
- runId: context.runId ?? input.runId,
8695
- },
8696
- {
8697
- tableNamespace: input.tableNamespace,
8698
- logicalTable: 'sheet_rows',
8699
- operations: ['rows.read', 'rows.upsert'],
8700
- limits: {
8701
- maxRows: runtimeDbSessionRowLimit(input.rows.length),
8702
- },
8703
- sheetContract,
8704
- },
8705
- ),
8706
- );
8799
+ const completionDiagnostics = createRuntimeMapCompletionDiagnostics();
8800
+ const completionStartedAt = Date.now();
8801
+ let completionOutcome: 'succeeded' | 'failed' = 'failed';
8802
+ let uniqueRowCount = 0;
8803
+ let completedRowCount = 0;
8804
+ let failedRowCount = 0;
8805
+ let chunkCount = 0;
8806
+ let updatedRowCount = 0;
8807
+ let fencedRowCount = 0;
8808
+ const logCompletion = () => {
8809
+ console.info(
8810
+ '[runtime-sheet-map-completion]',
8811
+ JSON.stringify({
8812
+ event: 'finished',
8813
+ outcome: completionOutcome,
8814
+ inputRows: input.rows.length,
8815
+ uniqueRows: uniqueRowCount,
8816
+ completedRows: completedRowCount,
8817
+ failedRows: failedRowCount,
8818
+ chunks: chunkCount,
8819
+ updatedRows: updatedRowCount,
8820
+ fencedRows: fencedRowCount,
8821
+ ...completionDiagnostics,
8822
+ totalMs: Date.now() - completionStartedAt,
8823
+ }),
8824
+ );
8825
+ };
8826
+ let session: RuntimePostgresSession;
8827
+ try {
8828
+ session = await measureRuntimeSheetPhase(
8829
+ completionDiagnostics,
8830
+ 'dbSessionAcquire',
8831
+ async () =>
8832
+ requireRuntimePostgresSession(
8833
+ await getRuntimeDbSession(
8834
+ {
8835
+ ...context,
8836
+ playName: context.playName,
8837
+ runId: context.runId ?? input.runId,
8838
+ },
8839
+ {
8840
+ tableNamespace: input.tableNamespace,
8841
+ logicalTable: 'sheet_rows',
8842
+ operations: ['rows.read', 'rows.upsert'],
8843
+ limits: {
8844
+ maxRows: runtimeDbSessionRowLimit(input.rows.length),
8845
+ },
8846
+ sheetContract,
8847
+ },
8848
+ ),
8849
+ ),
8850
+ );
8851
+ } catch (error) {
8852
+ logCompletion();
8853
+ throw error;
8854
+ }
8707
8855
 
8708
8856
  // Dedupe by key. Last write wins on payload, status, and error.
8709
8857
  const uniqueRows = new Map<string, RuntimeApiRowRecord>();
@@ -8718,7 +8866,10 @@ export async function completeRuntimeMapRows(
8718
8866
  if (!resolvedKey) continue;
8719
8867
  uniqueRows.set(resolvedKey, { ...row, key: resolvedKey });
8720
8868
  }
8869
+ uniqueRowCount = uniqueRows.size;
8721
8870
  if (uniqueRows.size === 0) {
8871
+ completionOutcome = 'succeeded';
8872
+ logCompletion();
8722
8873
  return classifyRuntimeMapRowsWrite({
8723
8874
  submittedKeys: [],
8724
8875
  updated: 0,
@@ -8735,6 +8886,9 @@ export async function completeRuntimeMapRows(
8735
8886
  });
8736
8887
  const chunks = chunkValues(completedRows, DIRECT_POSTGRES_BATCH_SIZE);
8737
8888
  const failedChunks = chunkValues(failedRows, DIRECT_POSTGRES_BATCH_SIZE);
8889
+ completedRowCount = completedRows.length;
8890
+ failedRowCount = failedRows.length;
8891
+ chunkCount = chunks.length + failedChunks.length;
8738
8892
  const needsTransaction = true;
8739
8893
  const outputFields = [...new Set(input.outputFields ?? [])];
8740
8894
  const attemptId = input.attemptId?.trim() || null;
@@ -8752,7 +8906,7 @@ export async function completeRuntimeMapRows(
8752
8906
  );
8753
8907
  }
8754
8908
 
8755
- return await withRuntimeSheetQueryClient(
8909
+ return withRuntimeSheetQueryClient(
8756
8910
  context,
8757
8911
  session,
8758
8912
  {
@@ -8761,6 +8915,7 @@ export async function completeRuntimeMapRows(
8761
8915
  sheetContract,
8762
8916
  transactional: needsTransaction,
8763
8917
  statementTimeoutMs: RUNTIME_SHEET_COMPLETION_STATEMENT_TIMEOUT_MS,
8918
+ completionDiagnostics,
8764
8919
  },
8765
8920
  async (client) => {
8766
8921
  const completed =
@@ -8781,15 +8936,20 @@ export async function completeRuntimeMapRows(
8781
8936
  ),
8782
8937
  outputFields,
8783
8938
  };
8784
- const updated = await completeRuntimeMapRowChunks(
8785
- client,
8786
- session,
8787
- chunkInput,
8939
+ const updated = await measureRuntimeSheetPhase(
8940
+ completionDiagnostics,
8941
+ 'mapRowsUpdate',
8942
+ () => completeRuntimeMapRowChunks(client, session, chunkInput),
8788
8943
  );
8789
- const inserted = await insertMissingCompletedMapRowChunks(
8790
- client,
8791
- session,
8792
- chunkInput,
8944
+ const inserted = await measureRuntimeSheetPhase(
8945
+ completionDiagnostics,
8946
+ 'missingRowsInsert',
8947
+ () =>
8948
+ insertMissingCompletedMapRowChunks(
8949
+ client,
8950
+ session,
8951
+ chunkInput,
8952
+ ),
8793
8953
  );
8794
8954
  return {
8795
8955
  updated: updated.updated + inserted.inserted,
@@ -8799,21 +8959,28 @@ export async function completeRuntimeMapRows(
8799
8959
  : { updated: 0, fencedRows: [] };
8800
8960
  const failed =
8801
8961
  failedRows.length > 0
8802
- ? await failRuntimeMapRowChunks(client, session, {
8803
- chunks: failedChunks,
8804
- physicalColumnProjections: projections,
8805
- forceTerminal: input.forceFailedRows === true,
8806
- runId: input.runId,
8807
- attemptId,
8808
- attemptOwnerRunId,
8809
- attemptExpiresAt,
8810
- attemptSeq,
8811
- writeVersion,
8812
- normalizedPlayName: normalizePlayNameForSheet(session.playName),
8813
- normalizedTableNamespace: normalizeTableNamespace(
8814
- input.tableNamespace,
8815
- ),
8816
- })
8962
+ ? await measureRuntimeSheetPhase(
8963
+ completionDiagnostics,
8964
+ 'failedRowsUpdate',
8965
+ () =>
8966
+ failRuntimeMapRowChunks(client, session, {
8967
+ chunks: failedChunks,
8968
+ physicalColumnProjections: projections,
8969
+ forceTerminal: input.forceFailedRows === true,
8970
+ runId: input.runId,
8971
+ attemptId,
8972
+ attemptOwnerRunId,
8973
+ attemptExpiresAt,
8974
+ attemptSeq,
8975
+ writeVersion,
8976
+ normalizedPlayName: normalizePlayNameForSheet(
8977
+ session.playName,
8978
+ ),
8979
+ normalizedTableNamespace: normalizeTableNamespace(
8980
+ input.tableNamespace,
8981
+ ),
8982
+ }),
8983
+ )
8817
8984
  : { updated: 0, fencedRows: [] };
8818
8985
  return classifyRuntimeMapRowsWrite({
8819
8986
  submittedKeys: uniqueRows.keys(),
@@ -8821,7 +8988,14 @@ export async function completeRuntimeMapRows(
8821
8988
  fencedRows: [...completed.fencedRows, ...failed.fencedRows],
8822
8989
  });
8823
8990
  },
8824
- );
8991
+ )
8992
+ .then((result) => {
8993
+ updatedRowCount = result.updated;
8994
+ fencedRowCount = result.fencedKeys.length;
8995
+ completionOutcome = 'succeeded';
8996
+ return result;
8997
+ })
8998
+ .finally(logCompletion);
8825
8999
  }
8826
9000
 
8827
9001
  /**
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Repair UTF-8 text that was decoded with the wrong single-byte code page and
3
+ * re-encoded ("mojibake"), the way Excel exports and copy-pasted sheets often
4
+ * arrive: "jérôme" as "jérôme" (read as Windows-1252) or "o’malley" as
5
+ * "o’malley" (read as Mac Roman).
6
+ *
7
+ * Why this exists: a 2026-08 ContactOut batch of 50 LinkedIn profiles was
8
+ * rejected outright because one slug carried "o’malley", and the same corrupt
9
+ * URLs had already been sent to LimaData and LeadMagic as paid misses. The
10
+ * corruption is in the customer's file, so the fix belongs where every CSV
11
+ * enters a Play: the runtime's CSV parser and the CLI's local CSV reader.
12
+ *
13
+ * Only runs of non-ASCII characters are touched, and a run is rewritten only
14
+ * when mapping it back through one of the two code pages yields strictly
15
+ * valid UTF-8. Correctly encoded text ("José", "Zoltán Erdős", CJK, emoji)
16
+ * never satisfies that test and passes through unchanged.
17
+ */
18
+
19
+ /** Windows-1252 bytes 0x80-0x9F that are not Latin-1 identity. */
20
+ const CP1252_HIGH_TO_BYTE = new Map<number, number>([
21
+ [0x20ac, 0x80],
22
+ [0x201a, 0x82],
23
+ [0x0192, 0x83],
24
+ [0x201e, 0x84],
25
+ [0x2026, 0x85],
26
+ [0x2020, 0x86],
27
+ [0x2021, 0x87],
28
+ [0x02c6, 0x88],
29
+ [0x2030, 0x89],
30
+ [0x0160, 0x8a],
31
+ [0x2039, 0x8b],
32
+ [0x0152, 0x8c],
33
+ [0x017d, 0x8e],
34
+ [0x2018, 0x91],
35
+ [0x2019, 0x92],
36
+ [0x201c, 0x93],
37
+ [0x201d, 0x94],
38
+ [0x2022, 0x95],
39
+ [0x2013, 0x96],
40
+ [0x2014, 0x97],
41
+ [0x02dc, 0x98],
42
+ [0x2122, 0x99],
43
+ [0x0161, 0x9a],
44
+ [0x203a, 0x9b],
45
+ [0x0153, 0x9c],
46
+ [0x017e, 0x9e],
47
+ [0x0178, 0x9f],
48
+ ]);
49
+
50
+ /** Mac OS Roman bytes 0x80-0xFF, in order (Python `bytes(range(128,256)).decode('mac_roman')`). */
51
+ const MAC_ROMAN_HIGH =
52
+ 'ÄÅÇÉÑÖÜáàâäãåçéè' +
53
+ 'êëíìîïñóòôöõúùûü' +
54
+ '†°¢£§•¶ß®©™´¨≠ÆØ' +
55
+ '∞±≤≥¥µ∂∑∏π∫ªºΩæø' +
56
+ '¿¡¬√ƒ≈∆«»… ÀÃÕŒœ' +
57
+ '–—“”‘’÷◊ÿŸ⁄€‹›fifl' +
58
+ '‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔ' +
59
+ 'ÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ';
60
+ const MAC_ROMAN_TO_BYTE = new Map<number, number>(
61
+ [...MAC_ROMAN_HIGH].map((ch, i) => [ch.codePointAt(0)!, 0x80 + i] as const),
62
+ );
63
+
64
+ const STRICT_UTF8 = new TextDecoder('utf-8', { fatal: true });
65
+
66
+ function cp1252Byte(code: number): number | null {
67
+ if (code < 0x100) return code;
68
+ return CP1252_HIGH_TO_BYTE.get(code) ?? null;
69
+ }
70
+
71
+ function macRomanByte(code: number): number | null {
72
+ if (code < 0x80) return code;
73
+ return MAC_ROMAN_TO_BYTE.get(code) ?? null;
74
+ }
75
+
76
+ function decodeAs(
77
+ run: string,
78
+ toByte: (code: number) => number | null,
79
+ ): string | null {
80
+ const bytes = new Uint8Array(run.length);
81
+ let i = 0;
82
+ for (const ch of run) {
83
+ const byte = toByte(ch.codePointAt(0)!);
84
+ if (byte === null) return null;
85
+ bytes[i++] = byte;
86
+ }
87
+ // A real mojibake run always encodes at least one multi-byte sequence, so it
88
+ // must contain a UTF-8 lead byte; a lone Latin-1 letter never does.
89
+ if (!bytes.subarray(0, i).some((b) => b >= 0xc2 && b <= 0xf4)) return null;
90
+ try {
91
+ return STRICT_UTF8.decode(bytes.subarray(0, i));
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ /** Maximal runs of non-ASCII characters are the only places mojibake can live. */
98
+ const NON_ASCII_RUN = /[^\u0000-\u007f]+/g;
99
+
100
+ /**
101
+ * Return `value` with mis-decoded UTF-8 runs repaired. Text that is already
102
+ * valid UTF-8 comes back identical (same string instance when nothing changed).
103
+ */
104
+ export function repairMisdecodedText(value: string): string {
105
+ // Fast path: pure ASCII cannot be mojibake.
106
+ if (!/[^\u0000-\u007f]/.test(value)) return value;
107
+ let changed = false;
108
+ const repaired = value.replace(NON_ASCII_RUN, (run) => {
109
+ const fixed = decodeAs(run, cp1252Byte) ?? decodeAs(run, macRomanByte);
110
+ if (fixed === null || fixed === run) return run;
111
+ changed = true;
112
+ return fixed;
113
+ });
114
+ return changed ? repaired : value;
115
+ }
116
+
117
+ /** Repair every string value of a parsed CSV record in place and return it. */
118
+ export function repairRecordText<T extends Record<string, unknown>>(
119
+ record: T,
120
+ ): T {
121
+ for (const key of Object.keys(record)) {
122
+ const value = record[key];
123
+ if (typeof value === 'string') {
124
+ (record as Record<string, unknown>)[key] = repairMisdecodedText(value);
125
+ }
126
+ }
127
+ return record;
128
+ }
129
+
130
+ /**
131
+ * `csv-parse` options every CSV entry point shares: strip a UTF-8 BOM, repair
132
+ * mojibake in header names (a corrupt header never matches a column mapping)
133
+ * and in every string cell.
134
+ */
135
+ export function csvInputTextOptions(): {
136
+ bom: true;
137
+ columns: (header: string[]) => string[];
138
+ on_record: <T extends Record<string, unknown>>(record: T) => T;
139
+ } {
140
+ return {
141
+ bom: true,
142
+ columns: (header) => header.map(repairMisdecodedText),
143
+ on_record: repairRecordText,
144
+ };
145
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+ var _chunkFBUWH67Yjs = require('./chunk-FBUWH67Y.js');
13
+ require('./chunk-ITOJRSOU.js');
14
+ require('./chunk-MO6QJPEB.js');
15
+ require('./chunk-XNYIZMNQ.js');
16
+ require('./chunk-UJCHR3J3.js');
17
+ require('./chunk-7P5AA3SN.js');
18
+ require('./chunk-OG64RBAC.js');
19
+ require('./chunk-COGTRVWW.js');
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+ exports.EXIT_WORKSPACE_MISMATCH = _chunkFBUWH67Yjs.EXIT_WORKSPACE_MISMATCH; exports.clearPendingClaim = _chunkFBUWH67Yjs.clearPendingClaim; exports.clearSatisfiedPendingClaim = _chunkFBUWH67Yjs.clearSatisfiedPendingClaim; exports.handleRegister = _chunkFBUWH67Yjs.handleRegister; exports.handleStatus = _chunkFBUWH67Yjs.handleStatus; exports.handleWait = _chunkFBUWH67Yjs.handleWait; exports.readPendingAuthClaim = _chunkFBUWH67Yjs.readPendingAuthClaim; exports.registerAuthCommands = _chunkFBUWH67Yjs.registerAuthCommands; exports.reportSetupCompleted = _chunkFBUWH67Yjs.reportSetupCompleted; exports.resolvePendingAuthClaimForResume = _chunkFBUWH67Yjs.resolvePendingAuthClaimForResume;
@@ -9,11 +9,11 @@ import {
9
9
  registerAuthCommands,
10
10
  reportSetupCompleted,
11
11
  resolvePendingAuthClaimForResume
12
- } from "./chunk-QV2WIEUP.mjs";
13
- import "./chunk-2Y2QZJBT.mjs";
14
- import "./chunk-W6LB26RC.mjs";
12
+ } from "./chunk-MLO7ZGSH.mjs";
13
+ import "./chunk-NTIWTBEH.mjs";
14
+ import "./chunk-JDSRPSES.mjs";
15
15
  import "./chunk-DLIRSHV6.mjs";
16
- import "./chunk-U5L6FIIL.mjs";
16
+ import "./chunk-EWYAS4WD.mjs";
17
17
  import "./chunk-NXGCO4JB.mjs";
18
18
  import "./chunk-WZYKRD7T.mjs";
19
19
  import "./chunk-KRD6H754.mjs";