@functional-systems/lambdadb-cli 0.1.0-dev.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/CHANGELOG.md +45 -0
- package/CONTRIBUTING.md +105 -0
- package/DESIGN.md +112 -0
- package/LICENSE +202 -0
- package/README.md +309 -0
- package/RELEASING.md +239 -0
- package/VALIDATION.md +222 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +208 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +105 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/import-workflow.d.ts +27 -0
- package/dist/import-workflow.js +60 -0
- package/dist/import-workflow.js.map +1 -0
- package/dist/input.d.ts +15 -0
- package/dist/input.js +127 -0
- package/dist/input.js.map +1 -0
- package/dist/output.d.ts +17 -0
- package/dist/output.js +68 -0
- package/dist/output.js.map +1 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.js +39 -0
- package/dist/runtime.js.map +1 -0
- package/examples/documents.jsonl +3 -0
- package/examples/index-config.json +4 -0
- package/examples/query.json +5 -0
- package/package.json +56 -0
- package/src/cli.ts +209 -0
- package/src/config.ts +96 -0
- package/src/errors.ts +45 -0
- package/src/import-workflow.ts +73 -0
- package/src/input.ts +105 -0
- package/src/output.ts +69 -0
- package/src/runtime.ts +40 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { LambdaDBError, ResponseValidationError, SDKValidationError } from '@functional-systems/lambdadb/models/errors';
|
|
2
|
+
export class InputError extends Error {
|
|
3
|
+
}
|
|
4
|
+
// Never serialize SDK errors: they can contain credentials, response bodies and signed URLs.
|
|
5
|
+
export function describeError(error) {
|
|
6
|
+
if (error instanceof InputError) {
|
|
7
|
+
return { code: 'INPUT_ERROR', message: error.message, hint: 'Check --help and the input file or configuration.' };
|
|
8
|
+
}
|
|
9
|
+
if (error instanceof ResponseValidationError) {
|
|
10
|
+
return { code: 'INVALID_RESPONSE', message: 'The API response does not match the SDK contract.', hint: 'Check SDK/API compatibility. A malformed write response does not prove the write failed.', httpStatus: error.statusCode };
|
|
11
|
+
}
|
|
12
|
+
if (error instanceof SDKValidationError) {
|
|
13
|
+
return { code: 'INPUT_ERROR', message: 'The request does not match the SDK contract.', hint: 'Check field names, types, index settings and ref selection.' };
|
|
14
|
+
}
|
|
15
|
+
if (error instanceof LambdaDBError) {
|
|
16
|
+
const status = error.statusCode;
|
|
17
|
+
const hint = status === 401 || status === 403
|
|
18
|
+
? 'Verify the selected API-key environment variable and its access to this project.'
|
|
19
|
+
: status === 404 ? 'Verify endpoint, project, collection and ref names.'
|
|
20
|
+
: status === 409 ? 'Inspect the existing resource before choosing a different name.'
|
|
21
|
+
: status === 429 ? 'Reduce load or retry later; a consistent-read overlay may exceed its limit.'
|
|
22
|
+
: status === 413 ? 'Reduce the batch size or use bulk mode where supported.'
|
|
23
|
+
: status >= 500 ? 'Check service availability. Inspect writes before retrying an uncertain mutation.'
|
|
24
|
+
: 'Check the request against the public API contract and collection schema.';
|
|
25
|
+
return { code: status === 401 || status === 403 ? 'AUTH_ERROR' : 'API_ERROR', message: `LambdaDB returned HTTP ${status}.`, httpStatus: status, hint };
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
code: 'REQUEST_FAILED',
|
|
29
|
+
message: 'The operation could not be completed or its response could not be verified.',
|
|
30
|
+
hint: 'Check endpoint, connectivity, timeout and SDK/API compatibility. Inspect writes before retrying.',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function isInputError(error) {
|
|
34
|
+
// SDKValidationError uses Symbol.hasInstance and also matches response validation errors.
|
|
35
|
+
return error instanceof InputError || (error instanceof SDKValidationError && !(error instanceof ResponseValidationError));
|
|
36
|
+
}
|
|
37
|
+
export function definitelyRejected(error) {
|
|
38
|
+
return isInputError(error) || (error instanceof LambdaDBError
|
|
39
|
+
&& error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 408);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAExH,MAAM,OAAO,UAAW,SAAQ,KAAK;CAAG;AAIxC,6FAA6F;AAC7F,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,mDAAmD,EAAE,CAAC;IACpH,CAAC;IACD,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;QAC7C,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,mDAAmD,EAAE,IAAI,EAAE,0FAA0F,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;IACpO,CAAC;IACD,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;QACxC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,8CAA8C,EAAE,IAAI,EAAE,6DAA6D,EAAE,CAAC;IAC/J,CAAC;IACD,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;YAC3C,CAAC,CAAC,kFAAkF;YACpF,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,qDAAqD;gBACxE,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,iEAAiE;oBACpF,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,6EAA6E;wBAChG,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,yDAAyD;4BAC5E,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,mFAAmF;gCACrG,CAAC,CAAC,0EAA0E,CAAC;QAC/E,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,0BAA0B,MAAM,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,6EAA6E;QACtF,IAAI,EAAE,kGAAkG;KACzG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,0FAA0F;IAC1F,OAAO,KAAK,YAAY,UAAU,IAAI,CAAC,KAAK,YAAY,kBAAkB,IAAI,CAAC,CAAC,KAAK,YAAY,uBAAuB,CAAC,CAAC,CAAC;AAC7H,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,aAAa;WACxD,KAAK,CAAC,UAAU,IAAI,GAAG,IAAI,KAAK,CAAC,UAAU,GAAG,GAAG,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ClientRequestOptions, CollectionDocs } from '@functional-systems/lambdadb';
|
|
2
|
+
import { type SafeError } from './errors.js';
|
|
3
|
+
import type { DocumentRow } from './input.js';
|
|
4
|
+
export type Batch = {
|
|
5
|
+
rows: DocumentRow[];
|
|
6
|
+
bytes: number;
|
|
7
|
+
};
|
|
8
|
+
export type BatchResult = {
|
|
9
|
+
batch: number;
|
|
10
|
+
firstLine: number;
|
|
11
|
+
lastLine: number;
|
|
12
|
+
documents: number;
|
|
13
|
+
state: 'accepted' | 'failed' | 'unknown';
|
|
14
|
+
error?: SafeError;
|
|
15
|
+
};
|
|
16
|
+
export type ImportResult = {
|
|
17
|
+
state: 'accepted' | 'failed' | 'partial' | 'unknown';
|
|
18
|
+
total: number;
|
|
19
|
+
accepted: number;
|
|
20
|
+
failed: number;
|
|
21
|
+
unknown: number;
|
|
22
|
+
notAttempted: number;
|
|
23
|
+
searchable: 'not_verified';
|
|
24
|
+
batches: BatchResult[];
|
|
25
|
+
};
|
|
26
|
+
export declare function planBatches(rows: DocumentRow[], branch: string, batchSize: number, maxBytes: number, bulk: boolean): Batch[];
|
|
27
|
+
export declare function importDocuments(docs: Pick<CollectionDocs, 'upsert' | 'bulkUpsertDocs'>, batches: Batch[], branch: string, bulk: boolean, options: ClientRequestOptions, progress?: (batch: BatchResult) => void): Promise<ImportResult>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { definitelyRejected, describeError, InputError } from './errors.js';
|
|
2
|
+
export function planBatches(rows, branch, batchSize, maxBytes, bulk) {
|
|
3
|
+
const overhead = Buffer.byteLength(JSON.stringify(bulk ? { docs: [] } : { docs: [], branch }));
|
|
4
|
+
const batches = [];
|
|
5
|
+
let current = { rows: [], bytes: overhead };
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
if (overhead + row.bytes > maxBytes)
|
|
8
|
+
throw new InputError(`JSONL line ${row.line} exceeds the batch byte limit. Increase the limit within the mode's supported range.`);
|
|
9
|
+
if (current.rows.length && (current.rows.length >= batchSize || current.bytes + 1 + row.bytes > maxBytes)) {
|
|
10
|
+
batches.push(current);
|
|
11
|
+
current = { rows: [], bytes: overhead };
|
|
12
|
+
}
|
|
13
|
+
current.bytes += row.bytes + (current.rows.length ? 1 : 0);
|
|
14
|
+
current.rows.push(row);
|
|
15
|
+
}
|
|
16
|
+
if (current.rows.length)
|
|
17
|
+
batches.push(current);
|
|
18
|
+
return batches;
|
|
19
|
+
}
|
|
20
|
+
// Domain workflow: no terminal I/O. Stop on the first failure, preserving acknowledged progress.
|
|
21
|
+
export async function importDocuments(docs, batches, branch, bulk, options, progress = () => { }) {
|
|
22
|
+
const total = batches.reduce((sum, b) => sum + b.rows.length, 0);
|
|
23
|
+
const result = {
|
|
24
|
+
state: 'accepted', total, accepted: 0, failed: 0, unknown: 0, notAttempted: total,
|
|
25
|
+
searchable: 'not_verified', batches: [],
|
|
26
|
+
};
|
|
27
|
+
for (const [index, batch] of batches.entries()) {
|
|
28
|
+
if (options.signal?.aborted) {
|
|
29
|
+
result.state = result.accepted > 0 ? 'partial' : 'failed';
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const item = {
|
|
33
|
+
batch: index + 1, firstLine: batch.rows[0].line, lastLine: batch.rows.at(-1).line,
|
|
34
|
+
documents: batch.rows.length, state: 'accepted',
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
const body = { docs: batch.rows.map(row => row.doc), branch };
|
|
38
|
+
// Mutations are never automatically retried: no public idempotency/receipt contract.
|
|
39
|
+
const requestOptions = { ...options, retries: { strategy: 'none' } };
|
|
40
|
+
if (bulk)
|
|
41
|
+
await docs.bulkUpsertDocs(body, requestOptions);
|
|
42
|
+
else
|
|
43
|
+
await docs.upsert(body, requestOptions);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
item.state = definitelyRejected(error) ? 'failed' : 'unknown';
|
|
47
|
+
item.error = describeError(error);
|
|
48
|
+
}
|
|
49
|
+
result[item.state] += item.documents;
|
|
50
|
+
result.notAttempted -= item.documents;
|
|
51
|
+
result.batches.push(item);
|
|
52
|
+
progress(item);
|
|
53
|
+
if (item.state !== 'accepted') {
|
|
54
|
+
result.state = item.state === 'unknown' ? 'unknown' : result.accepted > 0 ? 'partial' : 'failed';
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=import-workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-workflow.js","sourceRoot":"","sources":["../src/import-workflow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAC;AAc5F,MAAM,UAAU,WAAW,CAAC,IAAmB,EAAE,MAAc,EAAE,SAAiB,EAAE,QAAgB,EAAE,IAAa;IACjH,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAU,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACnD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,QAAQ,GAAG,GAAG,CAAC,KAAK,GAAG,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,cAAc,GAAG,CAAC,IAAI,sFAAsF,CAAC,CAAC;QACxK,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,QAAQ,CAAC,EAAE,CAAC;YAC1G,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;QAC1C,CAAC;QACD,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAuD,EACvD,OAAgB,EAAE,MAAc,EAAE,IAAa,EAAE,OAA6B,EAC9E,WAAyC,GAAG,EAAE,GAAE,CAAC;IAEjD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACjE,MAAM,MAAM,GAAiB;QAC3B,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK;QACjF,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE;KACxC,CAAC;IACF,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC5B,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC1D,MAAM;QACR,CAAC;QACD,MAAM,IAAI,GAAgB;YACxB,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI;YACnF,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU;SAChD,CAAC;QACF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;YAC9D,qFAAqF;YACrF,MAAM,cAAc,GAAyB,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC;YAC3F,IAAI,IAAI;gBAAE,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;;gBACrD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9D,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC;QACrC,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC9B,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;YACjG,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CreateCollectionInput, QueryCollectionInput, ReadRef } from '@functional-systems/lambdadb';
|
|
2
|
+
export declare const MAX_INPUT_BYTES: number;
|
|
3
|
+
export declare const MAX_INPUT_DOCUMENTS = 100000;
|
|
4
|
+
export declare function readText(path: string, limit?: number): Promise<string>;
|
|
5
|
+
export declare function object(value: unknown, label: string): Record<string, unknown>;
|
|
6
|
+
export declare function readJson(path: string): Promise<Record<string, unknown>>;
|
|
7
|
+
export declare function parseRef(value: string): ReadRef;
|
|
8
|
+
export declare function queryInput(body: Record<string, unknown>, ref: ReadRef): QueryCollectionInput;
|
|
9
|
+
export declare function createInput(collectionName: string, indexConfigs: Record<string, unknown>): CreateCollectionInput;
|
|
10
|
+
export type DocumentRow = {
|
|
11
|
+
line: number;
|
|
12
|
+
doc: Record<string, unknown>;
|
|
13
|
+
bytes: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function readJsonl(path: string): Promise<DocumentRow[]>;
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { open } from 'node:fs/promises';
|
|
2
|
+
import { TextDecoder } from 'node:util';
|
|
3
|
+
import { createCollectionRequestToJSON, queryCollectionRequestBodyToJSON } from '@functional-systems/lambdadb/models/operations';
|
|
4
|
+
import { InputError } from './errors.js';
|
|
5
|
+
import { name } from './config.js';
|
|
6
|
+
export const MAX_INPUT_BYTES = 64 * 1024 * 1024;
|
|
7
|
+
export const MAX_INPUT_DOCUMENTS = 100_000;
|
|
8
|
+
export async function readText(path, limit = MAX_INPUT_BYTES) {
|
|
9
|
+
let file;
|
|
10
|
+
try {
|
|
11
|
+
file = await open(path, 'r');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new InputError('Cannot open input file.');
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
if (!(await file.stat()).isFile())
|
|
18
|
+
throw new InputError('Input must be a regular file.');
|
|
19
|
+
// Read one immutable in-memory input snapshot; never submit a partially validated file.
|
|
20
|
+
const chunks = [];
|
|
21
|
+
let size = 0;
|
|
22
|
+
for await (const chunk of file.createReadStream({ autoClose: false })) {
|
|
23
|
+
size += chunk.length;
|
|
24
|
+
if (size > limit)
|
|
25
|
+
throw new InputError(`Input exceeds the ${limit}-byte local limit. Split the file first.`);
|
|
26
|
+
chunks.push(chunk);
|
|
27
|
+
}
|
|
28
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error instanceof InputError)
|
|
32
|
+
throw error;
|
|
33
|
+
throw new InputError('Cannot read input as UTF-8.');
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await file.close();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function object(value, label) {
|
|
40
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
41
|
+
throw new InputError(`${label} must be a JSON object.`);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
export async function readJson(path) {
|
|
45
|
+
const text = await readText(path, 4 * 1024 * 1024);
|
|
46
|
+
let value;
|
|
47
|
+
try {
|
|
48
|
+
value = JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new InputError('Input file contains invalid JSON.');
|
|
52
|
+
}
|
|
53
|
+
return object(value, 'Input');
|
|
54
|
+
}
|
|
55
|
+
export function parseRef(value) {
|
|
56
|
+
const [kind, refName, extra] = value.split(':');
|
|
57
|
+
if (!['branch', 'tag', 'alias'].includes(kind ?? '') || extra !== undefined) {
|
|
58
|
+
throw new InputError('Ref must be branch:NAME, tag:NAME or alias:NAME.');
|
|
59
|
+
}
|
|
60
|
+
return { kind: kind, name: name(refName, 'Ref name') };
|
|
61
|
+
}
|
|
62
|
+
export function queryInput(body, ref) {
|
|
63
|
+
if (body.ref !== undefined) {
|
|
64
|
+
const supplied = object(body.ref, 'File ref');
|
|
65
|
+
if (Object.keys(supplied).some(k => !['kind', 'name'].includes(k)) || supplied.kind !== ref.kind || supplied.name !== ref.name) {
|
|
66
|
+
throw new InputError('File ref conflicts with --ref. Use the same explicit target in both.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const input = { ...body, ref };
|
|
70
|
+
// Public API accepts null size, while SDK 0.5.1 only accepts undefined/number.
|
|
71
|
+
if (input.size === null)
|
|
72
|
+
delete input.size;
|
|
73
|
+
if (input.size !== undefined && (!Number.isInteger(input.size) || Number(input.size) < 1 || Number(input.size) > 100)) {
|
|
74
|
+
throw new InputError('Query size must be an integer from 1 to 100, null or omitted.');
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
queryCollectionRequestBodyToJSON(input);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new InputError('Invalid query request. Check request fields and types; consistentRead:true requires a branch ref.');
|
|
81
|
+
}
|
|
82
|
+
return input;
|
|
83
|
+
}
|
|
84
|
+
export function createInput(collectionName, indexConfigs) {
|
|
85
|
+
const input = { collectionName, indexConfigs };
|
|
86
|
+
try {
|
|
87
|
+
createCollectionRequestToJSON(input);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new InputError('Invalid index configuration. Provide a nonempty field-to-index map matching the SDK contract.');
|
|
91
|
+
}
|
|
92
|
+
return input;
|
|
93
|
+
}
|
|
94
|
+
export async function readJsonl(path) {
|
|
95
|
+
const text = await readText(path);
|
|
96
|
+
const rows = [];
|
|
97
|
+
// Do not allocate an array entry for every physical line (including blank ones).
|
|
98
|
+
let offset = 0;
|
|
99
|
+
let lineNumber = 0;
|
|
100
|
+
while (offset < text.length) {
|
|
101
|
+
const newline = text.indexOf('\n', offset);
|
|
102
|
+
const end = newline === -1 ? text.length : newline;
|
|
103
|
+
const line = text.slice(offset, end);
|
|
104
|
+
offset = end + 1;
|
|
105
|
+
lineNumber++;
|
|
106
|
+
if (!line.trim())
|
|
107
|
+
continue;
|
|
108
|
+
if (rows.length >= MAX_INPUT_DOCUMENTS) {
|
|
109
|
+
throw new InputError(`JSONL exceeds the ${MAX_INPUT_DOCUMENTS}-document local limit at line ${lineNumber}. Split the file first.`);
|
|
110
|
+
}
|
|
111
|
+
let doc;
|
|
112
|
+
try {
|
|
113
|
+
doc = object(JSON.parse(line), 'Document');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new InputError(`JSONL line ${lineNumber} must be a JSON object.`);
|
|
117
|
+
}
|
|
118
|
+
if ('id' in doc && (typeof doc.id !== 'string' || doc.id.length === 0)) {
|
|
119
|
+
throw new InputError(`JSONL line ${lineNumber} has an invalid id; use a nonempty string or omit it.`);
|
|
120
|
+
}
|
|
121
|
+
rows.push({ line: lineNumber, doc, bytes: Buffer.byteLength(JSON.stringify(doc)) });
|
|
122
|
+
}
|
|
123
|
+
if (rows.length === 0)
|
|
124
|
+
throw new InputError('JSONL input contains no documents.');
|
|
125
|
+
return rows;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"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"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SafeError } from './errors.js';
|
|
2
|
+
export type Envelope = {
|
|
3
|
+
schemaVersion: 1;
|
|
4
|
+
command: string;
|
|
5
|
+
ok: boolean;
|
|
6
|
+
target?: Record<string, unknown>;
|
|
7
|
+
data?: unknown;
|
|
8
|
+
error?: SafeError;
|
|
9
|
+
};
|
|
10
|
+
export declare class Output {
|
|
11
|
+
json: boolean;
|
|
12
|
+
private secrets;
|
|
13
|
+
protect(secret: string | undefined): void;
|
|
14
|
+
private redact;
|
|
15
|
+
diagnostic(message: string): void;
|
|
16
|
+
emit(envelope: Envelope, summary: string): void;
|
|
17
|
+
}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// These values are CLI protocol tokens, never arbitrary API or document data.
|
|
2
|
+
const protocolValues = new Set([
|
|
3
|
+
'command', 'error.code', 'target.ref.kind',
|
|
4
|
+
'data.state', 'data.searchable', 'data.batches.*.state', 'data.batches.*.error.code',
|
|
5
|
+
'data.checks.*.name', 'data.checks.*.status',
|
|
6
|
+
]);
|
|
7
|
+
// These document/metadata maps carry user-defined field names in MVP responses.
|
|
8
|
+
const freeFormMaps = new Set(['doc', 'indexConfigs', 'tags']);
|
|
9
|
+
export class Output {
|
|
10
|
+
json = false;
|
|
11
|
+
secrets = new Set();
|
|
12
|
+
protect(secret) { if (secret)
|
|
13
|
+
this.secrets.add(secret); }
|
|
14
|
+
redact(text) {
|
|
15
|
+
for (const secret of this.secrets)
|
|
16
|
+
text = text.split(secret).join('[REDACTED]');
|
|
17
|
+
return text;
|
|
18
|
+
}
|
|
19
|
+
diagnostic(message) {
|
|
20
|
+
// eslint-disable-next-line no-control-regex -- Strip terminal control bytes from diagnostics.
|
|
21
|
+
process.stderr.write(`${this.redact(message).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, '')}\n`);
|
|
22
|
+
}
|
|
23
|
+
emit(envelope, summary) {
|
|
24
|
+
// Preserve the protocol while redacting variable values and arbitrary map keys.
|
|
25
|
+
// Normalize dates first, and redact before serialization to preserve valid JSON.
|
|
26
|
+
const sanitize = (value, path = '', freeForm = false) => {
|
|
27
|
+
if (typeof value === 'string')
|
|
28
|
+
return !freeForm && protocolValues.has(path) ? value : this.redact(value);
|
|
29
|
+
if (Array.isArray(value))
|
|
30
|
+
return value.map(item => sanitize(item, `${path}.*`, freeForm));
|
|
31
|
+
if (value && typeof value === 'object') {
|
|
32
|
+
const entries = Object.entries(value).map(([key, item]) => ({ key, item, name: freeForm ? this.redact(key) : key }));
|
|
33
|
+
// Reserve every base name, including unchanged keys that appear later.
|
|
34
|
+
// Only renamed keys receive suffixes; unrelated field names stay intact.
|
|
35
|
+
const reserved = new Set(entries.map(entry => entry.name));
|
|
36
|
+
const used = new Set(entries.filter(entry => entry.key === entry.name).map(entry => entry.name));
|
|
37
|
+
const suffixes = new Map();
|
|
38
|
+
return Object.fromEntries(entries.map(({ key, item, name }) => {
|
|
39
|
+
let unique = name;
|
|
40
|
+
if (key !== name && used.has(unique)) {
|
|
41
|
+
let suffix = suffixes.get(name) ?? 1;
|
|
42
|
+
do {
|
|
43
|
+
unique = `${name}#${suffix++}`;
|
|
44
|
+
} while (reserved.has(unique) || used.has(unique));
|
|
45
|
+
suffixes.set(name, suffix);
|
|
46
|
+
}
|
|
47
|
+
used.add(unique);
|
|
48
|
+
return [unique, sanitize(item, path ? `${path}.${key}` : key, freeForm || freeFormMaps.has(key))];
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
};
|
|
53
|
+
const serialized = JSON.stringify(sanitize(JSON.parse(JSON.stringify(envelope))), null, this.json ? undefined : 2);
|
|
54
|
+
if (this.json)
|
|
55
|
+
process.stdout.write(`${serialized}\n`);
|
|
56
|
+
else {
|
|
57
|
+
const safe = JSON.parse(serialized);
|
|
58
|
+
process.stdout.write(`${this.redact(summary)}\n`);
|
|
59
|
+
if (safe.target)
|
|
60
|
+
process.stdout.write(`${JSON.stringify(safe.target)}\n`);
|
|
61
|
+
if (safe.data !== undefined)
|
|
62
|
+
process.stdout.write(`${JSON.stringify(safe.data, null, 2)}\n`);
|
|
63
|
+
}
|
|
64
|
+
if (envelope.error)
|
|
65
|
+
this.diagnostic(`${envelope.error.message} ${envelope.error.hint}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LambdaDBClient, type ClientRequestOptions } from '@functional-systems/lambdadb';
|
|
2
|
+
import type { Config } from './config.js';
|
|
3
|
+
export declare function runtime(config: Config): {
|
|
4
|
+
client: LambdaDBClient;
|
|
5
|
+
options: ClientRequestOptions;
|
|
6
|
+
close(): void;
|
|
7
|
+
};
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { HTTPClient, LambdaDBClient } from '@functional-systems/lambdadb';
|
|
2
|
+
export function runtime(config) {
|
|
3
|
+
// A no-op logger still makes SDK 0.5.1 clone and drain request/response bodies.
|
|
4
|
+
// Disable its environment-controlled logger before construction instead.
|
|
5
|
+
delete process.env.LAMBDADB_DEBUG;
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
const interrupt = () => controller.abort();
|
|
8
|
+
// Keep the command deadline alive across sequential SDK requests and transfers.
|
|
9
|
+
const deadline = setTimeout(() => controller.abort(new DOMException('Command deadline exceeded', 'TimeoutError')), config.timeoutMs);
|
|
10
|
+
deadline.unref();
|
|
11
|
+
process.once('SIGINT', interrupt);
|
|
12
|
+
process.once('SIGTERM', interrupt);
|
|
13
|
+
const options = {
|
|
14
|
+
signal: controller.signal, timeoutMs: config.timeoutMs,
|
|
15
|
+
retries: {
|
|
16
|
+
strategy: 'backoff', retryConnectionErrors: true,
|
|
17
|
+
backoff: { initialInterval: 200, maxInterval: 500, exponent: 1.5, maxElapsedTime: Math.min(2000, config.timeoutMs) },
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
// Bind the command signal at fetch dispatch as well as in SDK options, instead
|
|
21
|
+
// of relying only on the SDK's cloned Request signal chain.
|
|
22
|
+
// Keep authenticated API and unauthenticated transfer clients separate.
|
|
23
|
+
const fetcher = (input, init) => fetch(input, { ...init, signal: controller.signal });
|
|
24
|
+
const client = new LambdaDBClient({
|
|
25
|
+
baseUrl: config.endpoint, projectName: config.project, projectApiKey: config.apiKey,
|
|
26
|
+
timeoutMs: config.timeoutMs,
|
|
27
|
+
httpClient: new HTTPClient({ fetcher }),
|
|
28
|
+
transferClient: new HTTPClient({ fetcher }),
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
client, options,
|
|
32
|
+
close() {
|
|
33
|
+
clearTimeout(deadline);
|
|
34
|
+
process.removeListener('SIGINT', interrupt);
|
|
35
|
+
process.removeListener('SIGTERM', interrupt);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAA2C,MAAM,8BAA8B,CAAC;AAGnH,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,gFAAgF;IAChF,yEAAyE;IACzE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC3C,gFAAgF;IAChF,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACrI,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACnC,MAAM,OAAO,GAAyB;QACpC,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS;QACtD,OAAO,EAAE;YACP,QAAQ,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI;YAChD,OAAO,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE;SACrH;KACF,CAAC;IACF,+EAA+E;IAC/E,4DAA4D;IAC5D,wEAAwE;IACxE,MAAM,OAAO,GAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/F,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM;QACnF,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;QACvC,cAAc,EAAE,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;KAC5C,CAAC,CAAC;IACH,OAAO;QACL,MAAM,EAAE,OAAO;QACf,KAAK;YACH,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAC5C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"id":"doc-1","text":"LambdaDB supports serverless search.","category":"database"}
|
|
2
|
+
{"id":"doc-2","text":"Developers can search documents using a shared CLI.","category":"developer-tools"}
|
|
3
|
+
{"id":"doc-3","text":"A successful write is accepted before committed search visibility is verified.","category":"database"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@functional-systems/lambdadb-cli",
|
|
3
|
+
"version": "0.1.0-dev.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Project-scoped LambdaDB command-line tools",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lambdadb": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"src",
|
|
13
|
+
"README.md",
|
|
14
|
+
"DESIGN.md",
|
|
15
|
+
"VALIDATION.md",
|
|
16
|
+
"CONTRIBUTING.md",
|
|
17
|
+
"examples",
|
|
18
|
+
"RELEASING.md",
|
|
19
|
+
"CHANGELOG.md"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.14.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc && node -e \"require('node:fs').chmodSync('dist/cli.js', 0o755)\"",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
28
|
+
"start": "node dist/cli.js",
|
|
29
|
+
"prepack": "npm run build",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"check:version": "node scripts/check-release.mjs",
|
|
32
|
+
"test:package": "node scripts/test-package.mjs",
|
|
33
|
+
"test:live": "npm run build && node --test test/live/*.test.mjs"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@functional-systems/lambdadb": "0.5.1",
|
|
37
|
+
"commander": "14.0.3"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eslint/js": "^10.0.1",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"eslint": "^10.10.0",
|
|
43
|
+
"globals": "^16.5.0",
|
|
44
|
+
"typescript": "~5.8.3",
|
|
45
|
+
"typescript-eslint": "^8.70.0"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/lambdadb/lambdadb-cli.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/lambdadb/lambdadb-cli#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/lambdadb/lambdadb-cli/issues"
|
|
54
|
+
},
|
|
55
|
+
"license": "Apache-2.0"
|
|
56
|
+
}
|