@rex0220/kintone-sql-tools 3.74.0 → 3.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/dist-cli/ksql.js +140 -11
- package/dist-engine/index.cjs +14 -14
- package/dist-engine/index.mjs +14 -14
- package/dist-engine/ksql-engine.umd.js +14 -14
- package/dist-engine/meta/bundle-baseline.json +7 -7
- package/dist-engine/meta/cjs.json +11 -11
- package/dist-engine/meta/esm.json +11 -11
- package/dist-engine/meta/umd.json +11 -11
- package/dist-flow/flow-library/importSources.d.ts +7 -0
- package/dist-flow/flow-library/index.d.ts +3 -2
- package/dist-flow/flow-library/publicTypes.d.ts +36 -0
- package/dist-flow/index.cjs +11 -11
- package/dist-flow/index.mjs +11 -11
- package/dist-flow/meta/cjs.json +35 -16
- package/dist-flow/meta/esm.json +37 -16
- package/dist-mcp/ksql-mcp.js +95 -13
- package/dist-mcpb/ksql-mcp.mcpb +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ npm パッケージは 2 つのサブパスを **semver 対象の公開 API**
|
|
|
267
267
|
| サブパス | 用途 | 主な export |
|
|
268
268
|
|---|---|---|
|
|
269
269
|
| `@rex0220/kintone-sql-tools/engine` | **read-only** のクエリ実行(ダッシュボード等)。書込 API は構造的に遮断 | `runQuery` / `runBatch` / `explainQuery` / `createReadonlyKintoneClient` / `KsqlEngineError` / `version` |
|
|
270
|
-
| `@rex0220/kintone-sql-tools/flow` | **Flow dialect 1**(→ [言語リファレンス §27](docs/ksql_language_reference.md))のスクリプト解析・検証・**文単位実行**(バッチランナー向け・書込可能) | `parseScript` / `validateScript` / `explainScript`(`asOf`/`timezone` 注入可) / `createExecutionContext`(`onChunkWritten`
|
|
270
|
+
| `@rex0220/kintone-sql-tools/flow` | **Flow dialect 1**(→ [言語リファレンス §27](docs/ksql_language_reference.md))のスクリプト解析・検証・**文単位実行**(バッチランナー向け・書込可能) | `parseScript` / `validateScript` / `explainScript`(`asOf`/`timezone` 注入可) / `createExecutionContext`(`onChunkWritten` 書込チャンク通知・`onImportSourceMaterialized` IMPORT receipt) / `executeStatement` / `previewStatement`(dry-run 差分プレビュー・書込 0 回) / `disposeExecutionContext` / `createImportSourceResolver` / `FlowImportProviderError` / `createKintoneClient` / `isDmlResult`(`FlowDmlResult` 型ガード) / `version` |
|
|
271
271
|
|
|
272
272
|
バッチ実行ランナー **kSQL Flow**(`/flow` API を使った公式ランナー・別リポジトリ): https://github.com/rex0220/ksql-flow
|
|
273
273
|
|
|
@@ -289,6 +289,45 @@ try {
|
|
|
289
289
|
}
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
+
`/flow` から named `IMPORT` source を使う場合は、全APIへ `enableImport: true` を明示し、
|
|
293
|
+
pathではなくlazy loaderが返す `Uint8Array` を登録します。省略または `false` は従来どおり
|
|
294
|
+
`KSQL1202` で拒否し、resolverを渡すだけでは有効になりません。
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import {
|
|
298
|
+
createImportSourceResolver,
|
|
299
|
+
parseScript,
|
|
300
|
+
createExecutionContext,
|
|
301
|
+
} from "@rex0220/kintone-sql-tools/flow";
|
|
302
|
+
|
|
303
|
+
const capability = { enableImport: true } as const;
|
|
304
|
+
const importSource = createImportSourceResolver([{
|
|
305
|
+
name: "orders",
|
|
306
|
+
loader: { async load() { return { bytes, encoding: "sjis" }; } },
|
|
307
|
+
}]);
|
|
308
|
+
const parsed = parseScript(sql, capability);
|
|
309
|
+
const ctx = createExecutionContext({
|
|
310
|
+
...capability, client, statements: parsed.statements, meta: parsed.meta, importSource,
|
|
311
|
+
onImportSourceMaterialized(info) {
|
|
312
|
+
// { statementIndex, name, kind, rows, encoding }
|
|
313
|
+
inputFiles.push(info);
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
source名は完全一致・case-sensitiveです。loaderは対象文の実行到達時だけ呼ばれます。
|
|
319
|
+
CSVの文字コードは `SQL ENCODING` > loader metadata > UTF-8、payload上限は10 MiBです。
|
|
320
|
+
path解決、通常ファイル・symlink・allowlist・hash検査、open/readは呼出側の責務であり、
|
|
321
|
+
engineはpathを受け取りません。providerは `FlowImportProviderError` でread不能または通常ファイル外を
|
|
322
|
+
分類でき、その他のsource境界エラーも `StatementResult.error.code` の安定codeで返ります。
|
|
323
|
+
|
|
324
|
+
`onImportSourceMaterialized` はdecode・raw materialize成功直後、projection・validation・
|
|
325
|
+
`ON ERROR SKIP` の選別・mutationより前に1回awaitされます。CSVの `rows` はheaderを除く
|
|
326
|
+
RFC 4180 data record数(subtable CSVは継続行を含む)、JSONはtop-level record数です。
|
|
327
|
+
CSVの `encoding` は上記優先順位の解決後、JSONは `"utf8"` です。callbackがthrow/rejectすると
|
|
328
|
+
当該文はerrorになり、その文のmutation APIは0回です。通知objectのkeyは
|
|
329
|
+
`statementIndex` / `name` / `kind` / `rows` / `encoding` の5つだけです。
|
|
330
|
+
|
|
292
331
|
読取上限は既定 10,000 件(`maxRecords`)です。一時テーブルの実体化には**独立の** `tempTableMaxRows`(既定 10,000 行・超過は常にエラー)が適用されるため、大きなバッチでは両方を併せて指定してください:
|
|
293
332
|
|
|
294
333
|
```ts
|
package/dist-cli/ksql.js
CHANGED
|
@@ -17606,19 +17606,86 @@ var ImportSourceError = class extends Error {
|
|
|
17606
17606
|
this.name = "ImportSourceError";
|
|
17607
17607
|
}
|
|
17608
17608
|
};
|
|
17609
|
+
var ImportSourceBoundaryError = class extends Error {
|
|
17610
|
+
constructor(code, message, cause) {
|
|
17611
|
+
super(`${code}: ${message}`);
|
|
17612
|
+
this.name = code;
|
|
17613
|
+
this.code = code;
|
|
17614
|
+
if (cause !== void 0) {
|
|
17615
|
+
Object.defineProperty(this, "cause", {
|
|
17616
|
+
value: cause,
|
|
17617
|
+
enumerable: false,
|
|
17618
|
+
configurable: false,
|
|
17619
|
+
writable: false
|
|
17620
|
+
});
|
|
17621
|
+
}
|
|
17622
|
+
}
|
|
17623
|
+
};
|
|
17624
|
+
function providerError(error) {
|
|
17625
|
+
if (error === null || typeof error !== "object") return void 0;
|
|
17626
|
+
const shaped = error;
|
|
17627
|
+
const code = shaped.code === "ImportSourceReadError" || shaped.code === "ImportSourceNotRegularFileError" ? shaped.code : shaped.name === "ImportSourceReadError" || shaped.name === "ImportSourceNotRegularFileError" ? shaped.name : void 0;
|
|
17628
|
+
if (!code) return void 0;
|
|
17629
|
+
const fallback = code === "ImportSourceReadError" ? "IMPORT source could not be read." : "IMPORT source is not a regular file.";
|
|
17630
|
+
return new ImportSourceBoundaryError(
|
|
17631
|
+
code,
|
|
17632
|
+
typeof shaped.message === "string" && shaped.message.length > 0 ? shaped.message : fallback,
|
|
17633
|
+
error
|
|
17634
|
+
);
|
|
17635
|
+
}
|
|
17636
|
+
function isUint8Array(value) {
|
|
17637
|
+
return value !== null && typeof value === "object" && ArrayBuffer.isView(value) && Object.prototype.toString.call(value) === "[object Uint8Array]";
|
|
17638
|
+
}
|
|
17609
17639
|
function resolveImportSource(name, resolver) {
|
|
17610
|
-
if (!resolver)
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17640
|
+
if (!resolver) {
|
|
17641
|
+
throw new ImportSourceBoundaryError(
|
|
17642
|
+
"ImportSourceNotSuppliedError",
|
|
17643
|
+
`the named IMPORT source ${JSON.stringify(name)} was not supplied.`
|
|
17644
|
+
);
|
|
17645
|
+
}
|
|
17646
|
+
try {
|
|
17647
|
+
const handle = resolver(name);
|
|
17648
|
+
if (!handle) {
|
|
17649
|
+
throw new ImportSourceBoundaryError(
|
|
17650
|
+
"ImportSourceNotSuppliedError",
|
|
17651
|
+
`the named IMPORT source ${JSON.stringify(name)} was not supplied.`
|
|
17652
|
+
);
|
|
17653
|
+
}
|
|
17654
|
+
if (typeof handle !== "object" || typeof handle.load !== "function") {
|
|
17655
|
+
throw new ImportSourceBoundaryError(
|
|
17656
|
+
"ImportSourceInvalidPayloadError",
|
|
17657
|
+
"resolver must return a handle with a load function."
|
|
17658
|
+
);
|
|
17659
|
+
}
|
|
17660
|
+
return handle;
|
|
17661
|
+
} catch (error) {
|
|
17662
|
+
if (error instanceof ImportSourceBoundaryError) throw error;
|
|
17663
|
+
throw providerError(error) ?? new ImportSourceBoundaryError("ImportSourceReadError", "IMPORT source resolution failed.", error);
|
|
17664
|
+
}
|
|
17614
17665
|
}
|
|
17615
17666
|
async function loadImportSource(handle, cache) {
|
|
17616
17667
|
let pending = cache.get(handle);
|
|
17617
17668
|
if (!pending) {
|
|
17618
|
-
pending = handle.load().
|
|
17619
|
-
|
|
17669
|
+
pending = Promise.resolve().then(() => handle.load()).catch((error) => {
|
|
17670
|
+
throw providerError(error) ?? new ImportSourceBoundaryError("ImportSourceReadError", "IMPORT source could not be read.", error);
|
|
17671
|
+
}).then((payload) => {
|
|
17672
|
+
if (payload === null || typeof payload !== "object" || !isUint8Array(payload.bytes)) {
|
|
17673
|
+
throw new ImportSourceBoundaryError(
|
|
17674
|
+
"ImportSourceInvalidPayloadError",
|
|
17675
|
+
"loader must return Uint8Array bytes."
|
|
17676
|
+
);
|
|
17677
|
+
}
|
|
17678
|
+
if (payload.encoding !== void 0 && payload.encoding !== "utf8" && payload.encoding !== "sjis") {
|
|
17679
|
+
throw new ImportSourceBoundaryError(
|
|
17680
|
+
"ImportSourceInvalidPayloadError",
|
|
17681
|
+
"loader returned an unsupported encoding."
|
|
17682
|
+
);
|
|
17683
|
+
}
|
|
17620
17684
|
if (payload.bytes.byteLength > IMPORT_MAX_BYTES) {
|
|
17621
|
-
throw new
|
|
17685
|
+
throw new ImportSourceBoundaryError(
|
|
17686
|
+
"ImportSourceTooLargeError",
|
|
17687
|
+
`source exceeds the ${IMPORT_MAX_BYTES} byte limit.`
|
|
17688
|
+
);
|
|
17622
17689
|
}
|
|
17623
17690
|
return payload;
|
|
17624
17691
|
});
|
|
@@ -17979,6 +18046,7 @@ function materializeJsonDmlSource(_source, payload, targets, maxRows) {
|
|
|
17979
18046
|
importPresence.push(present);
|
|
17980
18047
|
});
|
|
17981
18048
|
return {
|
|
18049
|
+
receipt: { rows: records.length, encoding: "utf8" },
|
|
17982
18050
|
rows,
|
|
17983
18051
|
columns: targets.map((target) => target.code),
|
|
17984
18052
|
columnMeta: new Map(targets.map((target) => [target.code, { fieldType: target.fieldType }])),
|
|
@@ -17988,8 +18056,9 @@ function materializeJsonDmlSource(_source, payload, targets, maxRows) {
|
|
|
17988
18056
|
|
|
17989
18057
|
// src/import/materializeDmlSource.ts
|
|
17990
18058
|
function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInfos, recordNumberSourceHeader) {
|
|
18059
|
+
const encoding = source.encoding ?? payload.encoding ?? "utf8";
|
|
17991
18060
|
const decoded = decodeCsv(payload.bytes, {
|
|
17992
|
-
encoding
|
|
18061
|
+
encoding,
|
|
17993
18062
|
hasHeader: source.hasHeader,
|
|
17994
18063
|
columns: source.columns
|
|
17995
18064
|
});
|
|
@@ -18054,6 +18123,7 @@ function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInf
|
|
|
18054
18123
|
return row;
|
|
18055
18124
|
});
|
|
18056
18125
|
return {
|
|
18126
|
+
receipt: { rows: decoded.rows.length, encoding },
|
|
18057
18127
|
rows: rows2,
|
|
18058
18128
|
columns: [...targetCodes],
|
|
18059
18129
|
columnMeta: new Map(targetCodes.map((code) => [code, { fieldType: infoByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }])),
|
|
@@ -18064,6 +18134,7 @@ function materializeCsvDmlSource(source, payload, maxRows, targetCodes, fieldInf
|
|
|
18064
18134
|
}
|
|
18065
18135
|
const rows = decoded.rows.map((values) => Object.fromEntries(decoded.columns.map((column, i) => [column, values[i]])));
|
|
18066
18136
|
return {
|
|
18137
|
+
receipt: { rows: decoded.rows.length, encoding },
|
|
18067
18138
|
rows,
|
|
18068
18139
|
columns: decoded.columns,
|
|
18069
18140
|
// CSV cells are decoded as strings; projections such as CAST may replace this metadata downstream.
|
|
@@ -18083,6 +18154,7 @@ function materializeJsonImportRecords(_source, payload, targets, maxParents, max
|
|
|
18083
18154
|
if (targetByCode.size !== targets.length) throw new ImportSourceError("IMPORT targets contain duplicates.");
|
|
18084
18155
|
let childTotal = 0;
|
|
18085
18156
|
return {
|
|
18157
|
+
receipt: { rows: decoded.length, encoding: "utf8" },
|
|
18086
18158
|
records: decoded.map((record, index) => {
|
|
18087
18159
|
const parentRow = index + 1;
|
|
18088
18160
|
for (const code of record.keys()) if (!targetByCode.has(code)) sourceFail(parentRow, code, "unknown key (not declared in INTO).");
|
|
@@ -18118,8 +18190,9 @@ function materializeJsonImportRecords(_source, payload, targets, maxParents, max
|
|
|
18118
18190
|
};
|
|
18119
18191
|
}
|
|
18120
18192
|
function materializeCliKintoneCsvImportRecords(source, payload, targets, replacementTables, maxParents, recordNumberSourceHeader) {
|
|
18193
|
+
const encoding = source.encoding ?? payload.encoding ?? "utf8";
|
|
18121
18194
|
const decoded = decodeCsv(payload.bytes, {
|
|
18122
|
-
encoding
|
|
18195
|
+
encoding,
|
|
18123
18196
|
hasHeader: source.hasHeader,
|
|
18124
18197
|
columns: source.columns
|
|
18125
18198
|
});
|
|
@@ -18175,7 +18248,7 @@ function materializeCliKintoneCsvImportRecords(source, payload, targets, replace
|
|
|
18175
18248
|
}
|
|
18176
18249
|
});
|
|
18177
18250
|
if (records.length === 0) throw new ImportSourceError("CSV has no parent rows.");
|
|
18178
|
-
return { records };
|
|
18251
|
+
return { receipt: { rows: decoded.rows.length, encoding }, records };
|
|
18179
18252
|
}
|
|
18180
18253
|
|
|
18181
18254
|
// src/import/importRecordValidation.ts
|
|
@@ -18466,6 +18539,7 @@ var materializedMetaBySelectResult = /* @__PURE__ */ new WeakMap();
|
|
|
18466
18539
|
var materializedMetaByValidationResult = /* @__PURE__ */ new WeakMap();
|
|
18467
18540
|
var importSourceByDmlStatement = /* @__PURE__ */ new WeakMap();
|
|
18468
18541
|
var statementEvaluationContextKey = /* @__PURE__ */ Symbol("statementEvaluationContext");
|
|
18542
|
+
var importSourceMaterializedCallbackKey = /* @__PURE__ */ Symbol("importSourceMaterializedCallback");
|
|
18469
18543
|
var nativeUpsertExecutionKey = /* @__PURE__ */ Symbol("nativeUpsertExecution");
|
|
18470
18544
|
var nativeUpsertExplainCapabilityKey = /* @__PURE__ */ Symbol("nativeUpsertExplainCapability");
|
|
18471
18545
|
function withNativeUpsertExecutionOption(options, enabled, explainClientCapability) {
|
|
@@ -18495,6 +18569,10 @@ function bindStatementEvaluationContext(options) {
|
|
|
18495
18569
|
function statementEvaluationContext(options) {
|
|
18496
18570
|
return options[statementEvaluationContextKey] ?? {};
|
|
18497
18571
|
}
|
|
18572
|
+
async function notifyImportSourceMaterialized(options, source, receipt) {
|
|
18573
|
+
const callback = options[importSourceMaterializedCallbackKey];
|
|
18574
|
+
if (callback) await callback({ name: source.sourceName, kind: source.kind, ...receipt });
|
|
18575
|
+
}
|
|
18498
18576
|
var defaultCacheContextByClient = /* @__PURE__ */ new WeakMap();
|
|
18499
18577
|
var nextDefaultCacheContextId = 1;
|
|
18500
18578
|
var nextCacheInvocationId = 1;
|
|
@@ -24705,6 +24783,7 @@ async function executeImport(stmt, client, options, cacheContext, tempTables) {
|
|
|
24705
24783
|
const numberPrecision = fieldInfos.some((info) => targetCodes.includes(info.code) && info.fieldType === "NUMBER") ? await getNumberPrecisionCached(stmt.appId, client, cacheContext) : void 0;
|
|
24706
24784
|
const payload = await loadImportSource(handle, /* @__PURE__ */ new Map());
|
|
24707
24785
|
const materialized = stmt.source.kind === "JSON" ? materializeJsonImportRecords(stmt.source, payload, targets, options.maxRecords ?? 1e4) : materializeCliKintoneCsvImportRecords(stmt.source, payload, targets, stmt.replaceSubtables ?? [], options.maxRecords ?? 1e4, stmt.recordNumberSourceHeader);
|
|
24786
|
+
await notifyImportSourceMaterialized(options, stmt.source, materialized.receipt);
|
|
24708
24787
|
const operation = stmt.source.kind === "CSV" ? "UPDATE" : stmt.keyFields ? "UPSERT" : "INSERT";
|
|
24709
24788
|
const prepared = prepareImportRecords(materialized, targets, fieldInfos, numberPrecision, operation);
|
|
24710
24789
|
if (stmt.source.kind === "CSV") return executeCsvSubtableReplacement(stmt, materialized, prepared, fieldInfos, client, options, tempTables);
|
|
@@ -25007,6 +25086,7 @@ async function executeImportRecordNumberUpdate(stmt, handle, client, options, ca
|
|
|
25007
25086
|
fieldInfos,
|
|
25008
25087
|
stmt.recordNumberSourceHeader
|
|
25009
25088
|
);
|
|
25089
|
+
await notifyImportSourceMaterialized(options, stmt.source, sourceTable.receipt);
|
|
25010
25090
|
const keyValues = sourceTable.recordNumberSourceValues;
|
|
25011
25091
|
if (!keyValues) throw new Error("InternalError: record-number source values were not materialized.");
|
|
25012
25092
|
const keyPlan = preflightImportRecordNumbers(keyValues, stmt.recordNumberSourceHeader);
|
|
@@ -25143,6 +25223,7 @@ async function materializeDmlSource(stmt, client, options, cacheContext, tempTab
|
|
|
25143
25223
|
const targetByCode = new Map((targetFields ?? []).map((info) => [info.code, info]));
|
|
25144
25224
|
const jsonTargets = stmt.fields.map((code) => ({ code, fieldType: targetByCode.get(code)?.fieldType ?? "SINGLE_LINE_TEXT" }));
|
|
25145
25225
|
const raw = imported.source.kind === "JSON" ? materializeJsonDmlSource(imported.source, payload, jsonTargets, rowLimit) : materializeCsvDmlSource(imported.source, payload, rowLimit, stmt.fields, targetFields);
|
|
25226
|
+
await notifyImportSourceMaterialized(options, imported.source, raw.receipt);
|
|
25146
25227
|
imported.audit = raw.importAudit;
|
|
25147
25228
|
if (imported.source.kind === "JSON") return raw;
|
|
25148
25229
|
if (!imported.source.projection) return raw;
|
|
@@ -29172,6 +29253,23 @@ function isSubtableRow(v) {
|
|
|
29172
29253
|
return typeof obj.id === "string" && typeof obj.value === "object" && obj.value !== null;
|
|
29173
29254
|
}
|
|
29174
29255
|
|
|
29256
|
+
// src/flow-library/importSources.ts
|
|
29257
|
+
var FlowImportProviderError = class extends Error {
|
|
29258
|
+
constructor(code, message, cause) {
|
|
29259
|
+
super(message);
|
|
29260
|
+
this.name = code;
|
|
29261
|
+
this.code = code;
|
|
29262
|
+
if (cause !== void 0) {
|
|
29263
|
+
Object.defineProperty(this, "cause", {
|
|
29264
|
+
value: cause,
|
|
29265
|
+
enumerable: false,
|
|
29266
|
+
configurable: false,
|
|
29267
|
+
writable: false
|
|
29268
|
+
});
|
|
29269
|
+
}
|
|
29270
|
+
}
|
|
29271
|
+
};
|
|
29272
|
+
|
|
29175
29273
|
// src/output/batchEnvelope.ts
|
|
29176
29274
|
function toMutationSummary(result) {
|
|
29177
29275
|
if (result.type === "INSERT") {
|
|
@@ -32960,7 +33058,38 @@ async function run() {
|
|
|
32960
33058
|
const importEnabled = Object.keys(args.importCsv).length > 0 || Object.keys(args.importJson).length > 0;
|
|
32961
33059
|
const importSource = importEnabled ? (name) => {
|
|
32962
33060
|
const sourcePath = args.importCsv[name] ?? args.importJson[name];
|
|
32963
|
-
|
|
33061
|
+
if (sourcePath === void 0) return void 0;
|
|
33062
|
+
return {
|
|
33063
|
+
load: async () => {
|
|
33064
|
+
let sourceStat;
|
|
33065
|
+
try {
|
|
33066
|
+
sourceStat = (0, import_fs2.statSync)(sourcePath);
|
|
33067
|
+
} catch (error) {
|
|
33068
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
33069
|
+
throw new FlowImportProviderError(
|
|
33070
|
+
"ImportSourceReadError",
|
|
33071
|
+
`failed to inspect IMPORT source "${sourcePath}": ${reason}`,
|
|
33072
|
+
error
|
|
33073
|
+
);
|
|
33074
|
+
}
|
|
33075
|
+
if (!sourceStat.isFile()) {
|
|
33076
|
+
throw new FlowImportProviderError(
|
|
33077
|
+
"ImportSourceNotRegularFileError",
|
|
33078
|
+
`IMPORT source "${sourcePath}" is not a regular file.`
|
|
33079
|
+
);
|
|
33080
|
+
}
|
|
33081
|
+
try {
|
|
33082
|
+
return { bytes: new Uint8Array((0, import_fs2.readFileSync)(sourcePath)) };
|
|
33083
|
+
} catch (error) {
|
|
33084
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
33085
|
+
throw new FlowImportProviderError(
|
|
33086
|
+
"ImportSourceReadError",
|
|
33087
|
+
`failed to read IMPORT source "${sourcePath}": ${reason}`,
|
|
33088
|
+
error
|
|
33089
|
+
);
|
|
33090
|
+
}
|
|
33091
|
+
}
|
|
33092
|
+
};
|
|
32964
33093
|
} : void 0;
|
|
32965
33094
|
const confirm = async (count, operation, context) => {
|
|
32966
33095
|
if (count > dmlMaxRows) {
|