@nyrra/prismantix-cli 0.0.1 → 0.0.2-next.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 +20 -28
- package/bin/dev.js +1 -3
- package/bin/run.js +1 -3
- package/dist/api.d.ts +201 -173
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +237 -110
- package/dist/command-support.d.ts +13 -0
- package/dist/command-support.d.ts.map +1 -0
- package/dist/command-support.js +40 -0
- package/dist/commands/object/details.d.ts +13 -0
- package/dist/commands/object/details.d.ts.map +1 -0
- package/dist/commands/object/details.js +28 -0
- package/dist/commands/object/list.d.ts +13 -0
- package/dist/commands/object/list.d.ts.map +1 -0
- package/dist/commands/object/list.js +27 -0
- package/dist/commands/ontology/list.d.ts +10 -0
- package/dist/commands/ontology/list.d.ts.map +1 -0
- package/dist/commands/ontology/list.js +25 -0
- package/dist/commands/records/cancel.d.ts +13 -0
- package/dist/commands/records/cancel.d.ts.map +1 -0
- package/dist/commands/records/cancel.js +21 -0
- package/dist/commands/records/generate.d.ts +15 -0
- package/dist/commands/records/generate.d.ts.map +1 -0
- package/dist/commands/records/generate.js +56 -0
- package/dist/commands/{download.d.ts → records/pull.d.ts} +5 -11
- package/dist/commands/records/pull.d.ts.map +1 -0
- package/dist/commands/records/pull.js +59 -0
- package/dist/commands/records/status.d.ts +13 -0
- package/dist/commands/records/status.d.ts.map +1 -0
- package/dist/commands/records/status.js +22 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +39 -0
- package/dist/generation.d.ts +13 -0
- package/dist/generation.d.ts.map +1 -0
- package/dist/generation.js +77 -0
- package/dist/io.d.ts.map +1 -1
- package/dist/io.js +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +10 -0
- package/dist/workflow.d.ts +3 -14
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +85 -61
- package/oclif.manifest.json +266 -40
- package/package.json +74 -74
- package/dist/commands/download.d.ts.map +0 -1
- package/dist/commands/download.js +0 -136
- package/dist/commands/guided.d.ts +0 -7
- package/dist/commands/guided.d.ts.map +0 -1
- package/dist/commands/guided.js +0 -15
- package/dist/ui/guided-flow.d.ts +0 -20
- package/dist/ui/guided-flow.d.ts.map +0 -1
- package/dist/ui/guided-flow.js +0 -156
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Command, Flags } from "@oclif/core";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { apiFlags, failCommand, printResult, requireValue, runApiEffect, validatePositiveInteger, } from "../../command-support.js";
|
|
5
|
+
import { DEFAULT_GENERATION_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS } from "../../constants.js";
|
|
6
|
+
import { createRecordGeneration, waitForRecordGeneration } from "../../generation.js";
|
|
7
|
+
export default class RecordsGenerate extends Command {
|
|
8
|
+
static description = "Append a complete ontology-sized default record set for a client.";
|
|
9
|
+
static flags = {
|
|
10
|
+
...apiFlags,
|
|
11
|
+
client: Flags.string({ description: "Client ID.", required: true }),
|
|
12
|
+
ontology: Flags.string({ description: "Registered ontology ID.", required: true }),
|
|
13
|
+
"poll-interval": Flags.integer({
|
|
14
|
+
default: DEFAULT_POLL_INTERVAL_MS,
|
|
15
|
+
description: "Polling interval in milliseconds.",
|
|
16
|
+
}),
|
|
17
|
+
timeout: Flags.integer({
|
|
18
|
+
default: DEFAULT_GENERATION_TIMEOUT_MS,
|
|
19
|
+
description: "Polling timeout in milliseconds (default: 30 minutes).",
|
|
20
|
+
}),
|
|
21
|
+
wait: Flags.boolean({
|
|
22
|
+
allowNo: true,
|
|
23
|
+
default: true,
|
|
24
|
+
description: "Wait for the operation to reach a terminal state.",
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(RecordsGenerate);
|
|
29
|
+
try {
|
|
30
|
+
validatePositiveInteger(flags["poll-interval"], "--poll-interval");
|
|
31
|
+
validatePositiveInteger(flags.timeout, "--timeout");
|
|
32
|
+
const clientId = requireValue(flags.client, "--client");
|
|
33
|
+
const ontologyId = requireValue(flags.ontology, "--ontology");
|
|
34
|
+
const idempotencyKey = randomUUID();
|
|
35
|
+
const printOperationId = (operationId) => this.logToStderr(operationId);
|
|
36
|
+
const result = await runApiEffect(Effect.gen(function* () {
|
|
37
|
+
const created = yield* createRecordGeneration(clientId, ontologyId, idempotencyKey);
|
|
38
|
+
yield* Effect.sync(() => printOperationId(created.operationId));
|
|
39
|
+
if (!flags.wait)
|
|
40
|
+
return created;
|
|
41
|
+
return yield* waitForRecordGeneration(created, {
|
|
42
|
+
pollIntervalMs: flags["poll-interval"],
|
|
43
|
+
timeoutMs: flags.timeout,
|
|
44
|
+
});
|
|
45
|
+
}), flags["api-base"]);
|
|
46
|
+
printResult(this, result, flags.json, () => {
|
|
47
|
+
this.log(`Record generation ${result.operationId}: ${result.status}`);
|
|
48
|
+
this.log(`Client: ${result.clientId}`);
|
|
49
|
+
this.log(`Ontology: ${result.ontologyId}`);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
failCommand(this, error, flags.json);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
|
-
export default class
|
|
2
|
+
export default class RecordsPull extends Command {
|
|
3
3
|
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
4
|
static flags: {
|
|
6
|
-
|
|
7
|
-
client: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
"export-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
-
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
5
|
+
client: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
6
|
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
7
|
"poll-interval": import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
8
|
table: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
tables: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
9
|
timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
|
|
10
|
+
"api-base": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
12
|
};
|
|
17
13
|
run(): Promise<void>;
|
|
18
|
-
private fail;
|
|
19
|
-
private printProgress;
|
|
20
14
|
private printSummary;
|
|
21
15
|
}
|
|
22
|
-
//# sourceMappingURL=
|
|
16
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/records/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAkB7C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,OAAO;IAC/C,OAAgB,WAAW,SAA6D;IACxF,OAAgB,KAAK;;;;;;;;MAgBnB;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBjC,OAAO,CAAC,YAAY;CAMpB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Command, Flags } from "@oclif/core";
|
|
2
|
+
import { Match } from "effect";
|
|
3
|
+
import { makePrismantixApiClientLayer } from "../../api.js";
|
|
4
|
+
import { apiFlags, failCommand, printResult, requireValue, validatePositiveInteger, } from "../../command-support.js";
|
|
5
|
+
import { DEFAULT_POLL_INTERVAL_MS, DEFAULT_PULL_TIMEOUT_MS } from "../../constants.js";
|
|
6
|
+
import { runCliEffect } from "../../runtime.js";
|
|
7
|
+
import { runDownloadWorkflowEffect, } from "../../workflow.js";
|
|
8
|
+
export default class RecordsPull extends Command {
|
|
9
|
+
static description = "Pull a client CSV-bundle export into a local directory.";
|
|
10
|
+
static flags = {
|
|
11
|
+
...apiFlags,
|
|
12
|
+
client: Flags.string({ description: "Client ID to export.", required: true }),
|
|
13
|
+
output: Flags.string({ description: "Output directory." }),
|
|
14
|
+
"poll-interval": Flags.integer({
|
|
15
|
+
default: DEFAULT_POLL_INTERVAL_MS,
|
|
16
|
+
description: "Polling interval in milliseconds.",
|
|
17
|
+
}),
|
|
18
|
+
table: Flags.string({
|
|
19
|
+
description: "Table to pull; repeat for multiple tables.",
|
|
20
|
+
multiple: true,
|
|
21
|
+
}),
|
|
22
|
+
timeout: Flags.integer({
|
|
23
|
+
default: DEFAULT_PULL_TIMEOUT_MS,
|
|
24
|
+
description: "Polling timeout in milliseconds.",
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(RecordsPull);
|
|
29
|
+
try {
|
|
30
|
+
validatePositiveInteger(flags["poll-interval"], "--poll-interval");
|
|
31
|
+
validatePositiveInteger(flags.timeout, "--timeout");
|
|
32
|
+
const clientId = requireValue(flags.client, "--client");
|
|
33
|
+
const result = await runCliEffect(runDownloadWorkflowEffect({
|
|
34
|
+
clientId,
|
|
35
|
+
outputDir: flags.output,
|
|
36
|
+
pollIntervalMs: flags["poll-interval"],
|
|
37
|
+
tables: flags.table,
|
|
38
|
+
timeoutMs: flags.timeout,
|
|
39
|
+
}, { onEvent: (event) => this.logToStderr(progressMessage(event)) }), makePrismantixApiClientLayer({ apiBaseUrl: flags["api-base"] }));
|
|
40
|
+
printResult(this, result, flags.json, () => this.printSummary(result));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
failCommand(this, error, flags.json);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
printSummary(result) {
|
|
47
|
+
this.log(`Pulled PrismantiX export ${result.exportId}`);
|
|
48
|
+
this.log(`Output: ${result.outputDir}`);
|
|
49
|
+
this.log(`Manifest: ${result.manifestPath}`);
|
|
50
|
+
this.log(`Files: ${result.files.length}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const progressMessage = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
54
|
+
created: (event) => `Queued export ${event.exportId} for ${event.clientId}.`,
|
|
55
|
+
"download-complete": (event) => `Pulled ${event.file.tableName}.`,
|
|
56
|
+
"download-start": (event) => `Pulling ${event.file.tableName}...`,
|
|
57
|
+
manifest: (event) => `Manifest ready: ${event.manifest.files.length} file(s).`,
|
|
58
|
+
poll: (event) => `Export ${event.status.exportId}: ${event.status.status}.`,
|
|
59
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from "@oclif/core";
|
|
2
|
+
export default class RecordsStatus extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
operationId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static flags: {
|
|
8
|
+
"api-base": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/records/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAE,MAAM,aAAa,CAAC;AAU5C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,OAAO;IACjD,OAAgB,IAAI;;MAAoD;IACxE,OAAgB,WAAW,SAAyC;IACpE,OAAgB,KAAK;;;MAAY;IAEpB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAajC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Args, Command } from "@oclif/core";
|
|
2
|
+
import { apiFlags, failCommand, printResult, requireValue, runApiEffect, } from "../../command-support.js";
|
|
3
|
+
import { getRecordGeneration } from "../../generation.js";
|
|
4
|
+
export default class RecordsStatus extends Command {
|
|
5
|
+
static args = { operationId: Args.string({ required: true }) };
|
|
6
|
+
static description = "Show a record-generation operation.";
|
|
7
|
+
static flags = apiFlags;
|
|
8
|
+
async run() {
|
|
9
|
+
const { args, flags } = await this.parse(RecordsStatus);
|
|
10
|
+
try {
|
|
11
|
+
const operationId = requireValue(args.operationId, "operation-id");
|
|
12
|
+
const result = await runApiEffect(getRecordGeneration(operationId), flags["api-base"]);
|
|
13
|
+
printResult(this, result, flags.json, () => {
|
|
14
|
+
this.log(`Record generation ${result.operationId}: ${result.status}`);
|
|
15
|
+
this.log(`Persisted: ${result.totals.persistedCount}/${result.totals.requestedCount}`);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
failCommand(this, error, flags.json);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const DEFAULT_API_BASE_URL = "https://api.prismantix.com";
|
|
2
2
|
export declare const DEFAULT_OUTPUT_ROOT = "prismantix-exports";
|
|
3
3
|
export declare const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const DEFAULT_PULL_TIMEOUT_MS = 120000;
|
|
5
|
+
export declare const DEFAULT_GENERATION_TIMEOUT_MS: number;
|
|
5
6
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AACxD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AACxD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAC/C,eAAO,MAAM,6BAA6B,QAAiB,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const DEFAULT_API_BASE_URL = "https://api.prismantix.com";
|
|
2
2
|
export const DEFAULT_OUTPUT_ROOT = "prismantix-exports";
|
|
3
3
|
export const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
4
|
-
export const
|
|
4
|
+
export const DEFAULT_PULL_TIMEOUT_MS = 120_000;
|
|
5
|
+
export const DEFAULT_GENERATION_TIMEOUT_MS = 30 * 60 * 1000;
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA,qBAAa,YAAa,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,SAAI;CAMvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,gBA8B5C"}
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Predicate } from "effect";
|
|
1
2
|
export class CliUserError extends Error {
|
|
2
3
|
code;
|
|
3
4
|
exitCode;
|
|
@@ -12,8 +13,46 @@ export function toCliUserError(error) {
|
|
|
12
13
|
if (error instanceof CliUserError) {
|
|
13
14
|
return error;
|
|
14
15
|
}
|
|
16
|
+
if (isCodedError(error)) {
|
|
17
|
+
return new CliUserError(error.code, error.message, error.exitCode);
|
|
18
|
+
}
|
|
19
|
+
if (hasTag(error, "ApiConfigurationError")) {
|
|
20
|
+
return new CliUserError("missing_api_key", error.message);
|
|
21
|
+
}
|
|
22
|
+
if (hasTag(error, "ApiHttpError")) {
|
|
23
|
+
return new CliUserError(codeForStatus(error.status), error.message);
|
|
24
|
+
}
|
|
25
|
+
if (hasTag(error, "ApiDecodeError")) {
|
|
26
|
+
return new CliUserError("unexpected_error", error.message);
|
|
27
|
+
}
|
|
28
|
+
if (hasTag(error, "ApiTransportError")) {
|
|
29
|
+
return new CliUserError("unexpected_error", error.message);
|
|
30
|
+
}
|
|
15
31
|
if (error instanceof Error) {
|
|
16
32
|
return new CliUserError("unexpected_error", error.message);
|
|
17
33
|
}
|
|
18
34
|
return new CliUserError("unexpected_error", String(error));
|
|
19
35
|
}
|
|
36
|
+
function hasTag(error, tag) {
|
|
37
|
+
return Predicate.hasProperty(error, "_tag") && error._tag === tag;
|
|
38
|
+
}
|
|
39
|
+
function codeForStatus(status) {
|
|
40
|
+
if (status === 401)
|
|
41
|
+
return "unauthorized";
|
|
42
|
+
if (status === 403)
|
|
43
|
+
return "forbidden";
|
|
44
|
+
if (status === 404)
|
|
45
|
+
return "not_found";
|
|
46
|
+
if (status === 409)
|
|
47
|
+
return "conflict";
|
|
48
|
+
if (status === 429)
|
|
49
|
+
return "rate_limited";
|
|
50
|
+
return "api_error";
|
|
51
|
+
}
|
|
52
|
+
function isCodedError(error) {
|
|
53
|
+
return (error instanceof Error &&
|
|
54
|
+
Predicate.hasProperty(error, "code") &&
|
|
55
|
+
typeof error.code === "string" &&
|
|
56
|
+
Predicate.hasProperty(error, "exitCode") &&
|
|
57
|
+
typeof error.exitCode === "number");
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type RecordGenerationCreated, type RecordGenerationStatus, PrismantixApiClient } from "./api.js";
|
|
3
|
+
export interface WaitForGenerationOptions {
|
|
4
|
+
readonly now?: () => number;
|
|
5
|
+
readonly pollIntervalMs: number;
|
|
6
|
+
readonly sleep?: (milliseconds: number) => Promise<void>;
|
|
7
|
+
readonly timeoutMs: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function createRecordGeneration(clientId: string, ontologyId: string, idempotencyKey: string): Effect.Effect<RecordGenerationCreated, import("./api.js").ApiConfigurationError | import("./api.js").ApiHttpError | import("./api.js").ApiTransportError | import("./api.js").ApiDecodeError, PrismantixApiClient>;
|
|
10
|
+
export declare function getRecordGeneration(operationId: string): Effect.Effect<RecordGenerationStatus, import("./api.js").ApiConfigurationError | import("./api.js").ApiHttpError | import("./api.js").ApiTransportError | import("./api.js").ApiDecodeError, PrismantixApiClient>;
|
|
11
|
+
export declare function cancelRecordGeneration(operationId: string): Effect.Effect<RecordGenerationStatus, import("./api.js").ApiConfigurationError | import("./api.js").ApiHttpError | import("./api.js").ApiTransportError | import("./api.js").ApiDecodeError, PrismantixApiClient>;
|
|
12
|
+
export declare function waitForRecordGeneration(created: RecordGenerationCreated, options: WaitForGenerationOptions): Effect.Effect<RecordGenerationStatus, unknown, PrismantixApiClient>;
|
|
13
|
+
//# sourceMappingURL=generation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../src/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,MAAM,EAAgB,MAAM,QAAQ,CAAC;AACnE,OAAO,EACN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,mBAAmB,EACnB,MAAM,UAAU,CAAC;AAKlB,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,sNAMtB;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,qNAKtD;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,qNAKzD;AAED,wBAAgB,uBAAuB,CACtC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,wBAAwB,uEASjC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Cause, Effect, Exit, Option } from "effect";
|
|
2
|
+
import { PrismantixApiClient, } from "./api.js";
|
|
3
|
+
import { CliUserError } from "./errors.js";
|
|
4
|
+
export function createRecordGeneration(clientId, ontologyId, idempotencyKey) {
|
|
5
|
+
return Effect.gen(function* () {
|
|
6
|
+
const api = yield* PrismantixApiClient;
|
|
7
|
+
return yield* api.createRecordGeneration(clientId, ontologyId, idempotencyKey);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export function getRecordGeneration(operationId) {
|
|
11
|
+
return Effect.gen(function* () {
|
|
12
|
+
const api = yield* PrismantixApiClient;
|
|
13
|
+
return yield* api.getRecordGeneration(operationId);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function cancelRecordGeneration(operationId) {
|
|
17
|
+
return Effect.gen(function* () {
|
|
18
|
+
const api = yield* PrismantixApiClient;
|
|
19
|
+
return yield* api.cancelRecordGeneration(operationId);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function waitForRecordGeneration(created, options) {
|
|
23
|
+
return Effect.gen(function* () {
|
|
24
|
+
const api = yield* PrismantixApiClient;
|
|
25
|
+
return yield* Effect.tryPromise({
|
|
26
|
+
try: () => waitForTerminalStatus(api, created.operationId, options),
|
|
27
|
+
catch: (error) => error,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function waitForTerminalStatus(api, operationId, options) {
|
|
32
|
+
const now = resolveNow(options.now);
|
|
33
|
+
const sleep = resolveSleep(options.sleep);
|
|
34
|
+
const startedAt = now();
|
|
35
|
+
for (;;) {
|
|
36
|
+
const status = await runClientEffect(api.getRecordGeneration(operationId));
|
|
37
|
+
if (isTerminal(status)) {
|
|
38
|
+
return requireCompletedGeneration(status);
|
|
39
|
+
}
|
|
40
|
+
throwIfTimedOut(operationId, now() - startedAt, options.timeoutMs);
|
|
41
|
+
await sleep(options.pollIntervalMs);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function resolveNow(now) {
|
|
45
|
+
return now ?? (() => Date.now());
|
|
46
|
+
}
|
|
47
|
+
function resolveSleep(sleep) {
|
|
48
|
+
return sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
49
|
+
}
|
|
50
|
+
function isTerminal(status) {
|
|
51
|
+
return ["canceled", "completed", "failed"].includes(status.status);
|
|
52
|
+
}
|
|
53
|
+
function requireCompletedGeneration(status) {
|
|
54
|
+
if (status.status === "completed")
|
|
55
|
+
return status;
|
|
56
|
+
if (status.status === "canceled") {
|
|
57
|
+
throw new CliUserError("record_generation_canceled", `Record generation ${status.operationId} was canceled.`);
|
|
58
|
+
}
|
|
59
|
+
const failureDetails = status.failure
|
|
60
|
+
? `${status.failure.message} (${status.failure.code})`
|
|
61
|
+
: "The API did not provide failure details.";
|
|
62
|
+
throw new CliUserError("record_generation_failed", `Record generation ${status.operationId} failed: ${failureDetails}`);
|
|
63
|
+
}
|
|
64
|
+
function throwIfTimedOut(operationId, elapsedMs, timeoutMs) {
|
|
65
|
+
if (elapsedMs < timeoutMs)
|
|
66
|
+
return;
|
|
67
|
+
throw new CliUserError("timeout", `Timed out waiting for record generation ${operationId}.`);
|
|
68
|
+
}
|
|
69
|
+
async function runClientEffect(effect) {
|
|
70
|
+
const exit = await Effect.runPromiseExit(effect);
|
|
71
|
+
if (Exit.isSuccess(exit))
|
|
72
|
+
return exit.value;
|
|
73
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
74
|
+
if (Option.isSome(failure))
|
|
75
|
+
throw failure.value;
|
|
76
|
+
throw Cause.squash(exit.cause);
|
|
77
|
+
}
|
package/dist/io.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,mBAkB7E"}
|
package/dist/io.js
CHANGED
|
@@ -15,6 +15,6 @@ export async function writeResponseBodyToFile(response, path) {
|
|
|
15
15
|
callback(null, chunk);
|
|
16
16
|
},
|
|
17
17
|
});
|
|
18
|
-
await pipeline(Readable.
|
|
18
|
+
await pipeline(Readable.from(response.body), byteCounter, createWriteStream(path));
|
|
19
19
|
return sizeBytes;
|
|
20
20
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAQ,KAAK,KAAK,EAAU,MAAM,QAAQ,CAAC;AAEjE,eAAO,MAAM,YAAY,GAAU,CAAC,EAAE,CAAC,EAAE,CAAC,EACzC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,eAQrC,CAAC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Cause, Effect, Exit, Option } from "effect";
|
|
2
|
+
export const runCliEffect = async (effect, layer) => {
|
|
3
|
+
const exit = await Effect.runPromiseExit(effect.pipe(Effect.provide(layer)));
|
|
4
|
+
if (Exit.isSuccess(exit))
|
|
5
|
+
return exit.value;
|
|
6
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
7
|
+
if (Option.isSome(failure))
|
|
8
|
+
throw failure.value;
|
|
9
|
+
throw Cause.squash(exit.cause);
|
|
10
|
+
};
|
package/dist/workflow.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { type ExportManifest, type ExportManifestFile, type ExportStatus, PrismantixApiClient } from "./api.js";
|
|
3
4
|
export interface DownloadWorkflowOptions {
|
|
4
|
-
readonly apiBaseUrl?: string;
|
|
5
|
-
readonly apiKey?: string;
|
|
6
5
|
readonly clientId?: string;
|
|
7
6
|
readonly exportId?: string;
|
|
8
7
|
readonly outputDir?: string;
|
|
@@ -12,7 +11,6 @@ export interface DownloadWorkflowOptions {
|
|
|
12
11
|
}
|
|
13
12
|
export interface DownloadWorkflowRuntime {
|
|
14
13
|
readonly cwd?: string;
|
|
15
|
-
readonly fetchImpl?: typeof fetch;
|
|
16
14
|
readonly mkdir?: typeof mkdir;
|
|
17
15
|
readonly now?: () => number;
|
|
18
16
|
readonly onEvent?: (event: WorkflowEvent) => void;
|
|
@@ -51,14 +49,5 @@ export interface DownloadResult {
|
|
|
51
49
|
readonly manifestPath: string;
|
|
52
50
|
readonly outputDir: string;
|
|
53
51
|
}
|
|
54
|
-
export
|
|
55
|
-
readonly fileCount: number;
|
|
56
|
-
readonly tables: readonly {
|
|
57
|
-
readonly rowCount?: number;
|
|
58
|
-
readonly tableName: string;
|
|
59
|
-
}[];
|
|
60
|
-
readonly totalRows: number;
|
|
61
|
-
}
|
|
62
|
-
export declare function runDownloadWorkflow(options: DownloadWorkflowOptions, runtime?: DownloadWorkflowRuntime): Promise<DownloadResult>;
|
|
63
|
-
export declare function summarizeManifest(manifest: ExportManifest): ManifestSummary;
|
|
52
|
+
export declare function runDownloadWorkflowEffect(options: DownloadWorkflowOptions, runtime?: DownloadWorkflowRuntime): Effect.Effect<DownloadResult, unknown, PrismantixApiClient>;
|
|
64
53
|
//# sourceMappingURL=workflow.d.ts.map
|
package/dist/workflow.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAuB,MAAM,EAAgB,MAAM,QAAQ,CAAC;AACnE,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,mBAAmB,EACnB,MAAM,UAAU,CAAC;AASlB,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;IACtC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnF;AAED,MAAM,MAAM,aAAa,GACtB;IACA,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;CACxB,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACrB,GACD;IACA,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CACzB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CAC/B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;CAClC,CAAC;AAEL,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,uBAAuB,EAChC,OAAO,GAAE,uBAA4B,+DASrC"}
|
package/dist/workflow.js
CHANGED
|
@@ -1,37 +1,32 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { Cause, Effect, Exit, Option } from "effect";
|
|
3
4
|
import { PrismantixApiClient, } from "./api.js";
|
|
4
|
-
import {
|
|
5
|
+
import { DEFAULT_OUTPUT_ROOT, DEFAULT_POLL_INTERVAL_MS, DEFAULT_PULL_TIMEOUT_MS, } from "./constants.js";
|
|
5
6
|
import { CliUserError } from "./errors.js";
|
|
6
7
|
import { writeResponseBodyToFile } from "./io.js";
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const now = runtime.now ?? (() => Date.now());
|
|
15
|
-
const mkdirImpl = runtime.mkdir ?? mkdir;
|
|
16
|
-
const writeFileImpl = runtime.writeFile ?? writeFile;
|
|
17
|
-
const writeResponseBody = runtime.writeResponseBody ?? writeResponseBodyToFile;
|
|
18
|
-
const emit = runtime.onEvent ?? (() => undefined);
|
|
19
|
-
const api = new PrismantixApiClient({
|
|
20
|
-
apiBaseUrl: options.apiBaseUrl ?? DEFAULT_API_BASE_URL,
|
|
21
|
-
apiKey,
|
|
22
|
-
fetchImpl: runtime.fetchImpl,
|
|
8
|
+
export function runDownloadWorkflowEffect(options, runtime = {}) {
|
|
9
|
+
return Effect.gen(function* () {
|
|
10
|
+
const api = yield* PrismantixApiClient;
|
|
11
|
+
return yield* Effect.tryPromise({
|
|
12
|
+
try: () => runDownloadWorkflowPromise(api, options, runtime),
|
|
13
|
+
catch: (error) => error,
|
|
14
|
+
});
|
|
23
15
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
16
|
+
}
|
|
17
|
+
const resolveWorkflowRuntime = (runtime) => ({
|
|
18
|
+
emit: runtime.onEvent ?? (() => undefined),
|
|
19
|
+
mkdirImpl: runtime.mkdir ?? mkdir,
|
|
20
|
+
now: runtime.now ?? (() => Date.now()),
|
|
21
|
+
sleep: runtime.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))),
|
|
22
|
+
writeFileImpl: runtime.writeFile ?? writeFile,
|
|
23
|
+
writeResponseBody: runtime.writeResponseBody ?? writeResponseBodyToFile,
|
|
24
|
+
});
|
|
25
|
+
async function runDownloadWorkflowPromise(api, options, runtime = {}) {
|
|
26
|
+
const { emit, mkdirImpl, now, sleep, writeFileImpl, writeResponseBody } = resolveWorkflowRuntime(runtime);
|
|
27
|
+
const resolvedExport = await resolveExport(api, options, emit);
|
|
28
|
+
let clientId = resolvedExport.clientId;
|
|
29
|
+
const exportId = resolvedExport.exportId;
|
|
35
30
|
const status = await waitForCompletedExport({
|
|
36
31
|
api,
|
|
37
32
|
exportId,
|
|
@@ -39,10 +34,10 @@ export async function runDownloadWorkflow(options, runtime = {}) {
|
|
|
39
34
|
onPoll: (polledStatus) => emit({ status: polledStatus, type: "poll" }),
|
|
40
35
|
pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
|
|
41
36
|
sleep,
|
|
42
|
-
timeoutMs: options.timeoutMs ??
|
|
37
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_PULL_TIMEOUT_MS,
|
|
43
38
|
});
|
|
44
39
|
clientId = status.clientId;
|
|
45
|
-
const manifest = await api.getManifest(exportId);
|
|
40
|
+
const manifest = await runClientEffect(api.getManifest(exportId));
|
|
46
41
|
emit({ manifest, type: "manifest" });
|
|
47
42
|
const outputDir = resolveOutputDir({
|
|
48
43
|
clientId,
|
|
@@ -55,26 +50,15 @@ export async function runDownloadWorkflow(options, runtime = {}) {
|
|
|
55
50
|
await writeFileImpl(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, {
|
|
56
51
|
encoding: "utf8",
|
|
57
52
|
});
|
|
58
|
-
const selectedFiles =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const writtenSizeBytes = await writeResponseBody(response, destinationPath);
|
|
68
|
-
const downloadedFile = {
|
|
69
|
-
path: destinationPath,
|
|
70
|
-
rowCount: file.rowCount,
|
|
71
|
-
sha256: file.sha256,
|
|
72
|
-
sizeBytes: file.sizeBytes ?? writtenSizeBytes,
|
|
73
|
-
tableName: file.tableName,
|
|
74
|
-
};
|
|
75
|
-
files.push(downloadedFile);
|
|
76
|
-
emit({ file: downloadedFile, type: "download-complete" });
|
|
77
|
-
}
|
|
53
|
+
const selectedFiles = requireSelectedFiles(manifest.files, options.tables ?? []);
|
|
54
|
+
const files = await downloadFiles({
|
|
55
|
+
api,
|
|
56
|
+
emit,
|
|
57
|
+
exportId,
|
|
58
|
+
files: selectedFiles,
|
|
59
|
+
outputDir,
|
|
60
|
+
writeResponseBody,
|
|
61
|
+
});
|
|
78
62
|
return {
|
|
79
63
|
clientId,
|
|
80
64
|
exportId,
|
|
@@ -83,16 +67,6 @@ export async function runDownloadWorkflow(options, runtime = {}) {
|
|
|
83
67
|
outputDir,
|
|
84
68
|
};
|
|
85
69
|
}
|
|
86
|
-
export function summarizeManifest(manifest) {
|
|
87
|
-
return {
|
|
88
|
-
fileCount: manifest.files.length,
|
|
89
|
-
tables: manifest.files.map((file) => ({
|
|
90
|
-
rowCount: file.rowCount,
|
|
91
|
-
tableName: file.tableName,
|
|
92
|
-
})),
|
|
93
|
-
totalRows: manifest.totalRows,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
70
|
function normalizeClientId(value) {
|
|
97
71
|
const normalized = value?.trim();
|
|
98
72
|
return normalized ? normalized.toUpperCase() : undefined;
|
|
@@ -100,7 +74,7 @@ function normalizeClientId(value) {
|
|
|
100
74
|
async function waitForCompletedExport(args) {
|
|
101
75
|
const startedAt = args.now();
|
|
102
76
|
for (;;) {
|
|
103
|
-
const status = await args.api.getExportStatus(args.exportId);
|
|
77
|
+
const status = await runClientEffect(args.api.getExportStatus(args.exportId));
|
|
104
78
|
args.onPoll(status);
|
|
105
79
|
if (status.status === "completed") {
|
|
106
80
|
return status;
|
|
@@ -114,6 +88,50 @@ async function waitForCompletedExport(args) {
|
|
|
114
88
|
await args.sleep(args.pollIntervalMs);
|
|
115
89
|
}
|
|
116
90
|
}
|
|
91
|
+
async function runClientEffect(effect) {
|
|
92
|
+
const exit = await Effect.runPromiseExit(effect);
|
|
93
|
+
if (Exit.isSuccess(exit))
|
|
94
|
+
return exit.value;
|
|
95
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
96
|
+
if (Option.isSome(failure))
|
|
97
|
+
throw failure.value;
|
|
98
|
+
throw Cause.squash(exit.cause);
|
|
99
|
+
}
|
|
100
|
+
async function resolveExport(api, options, emit) {
|
|
101
|
+
const clientId = normalizeClientId(options.clientId);
|
|
102
|
+
const exportId = options.exportId?.trim();
|
|
103
|
+
if (exportId)
|
|
104
|
+
return { clientId, exportId };
|
|
105
|
+
if (!clientId) {
|
|
106
|
+
throw new CliUserError("missing_client", "Pass --client when creating a new export.");
|
|
107
|
+
}
|
|
108
|
+
const created = await runClientEffect(api.createExport(clientId));
|
|
109
|
+
emit({
|
|
110
|
+
clientId: created.clientId,
|
|
111
|
+
exportId: created.exportId,
|
|
112
|
+
type: "created",
|
|
113
|
+
});
|
|
114
|
+
return { clientId: created.clientId, exportId: created.exportId };
|
|
115
|
+
}
|
|
116
|
+
async function downloadFiles(args) {
|
|
117
|
+
const downloadedFiles = [];
|
|
118
|
+
for (const file of args.files) {
|
|
119
|
+
args.emit({ file, type: "download-start" });
|
|
120
|
+
const destinationPath = join(args.outputDir, basename(file.fileName));
|
|
121
|
+
const response = await runClientEffect(args.api.getFile(args.exportId, file.tableName));
|
|
122
|
+
const writtenSizeBytes = await args.writeResponseBody(response, destinationPath);
|
|
123
|
+
const downloadedFile = {
|
|
124
|
+
path: destinationPath,
|
|
125
|
+
rowCount: file.rowCount,
|
|
126
|
+
sha256: file.sha256,
|
|
127
|
+
sizeBytes: file.sizeBytes ?? writtenSizeBytes,
|
|
128
|
+
tableName: file.tableName,
|
|
129
|
+
};
|
|
130
|
+
downloadedFiles.push(downloadedFile);
|
|
131
|
+
args.emit({ file: downloadedFile, type: "download-complete" });
|
|
132
|
+
}
|
|
133
|
+
return downloadedFiles;
|
|
134
|
+
}
|
|
117
135
|
function resolveOutputDir(args) {
|
|
118
136
|
return resolve(args.cwd, args.outputDir ?? join(DEFAULT_OUTPUT_ROOT, args.clientId, args.exportId));
|
|
119
137
|
}
|
|
@@ -128,3 +146,9 @@ function selectManifestFiles(files, tables) {
|
|
|
128
146
|
const allowedTables = new Set(normalizedTables);
|
|
129
147
|
return files.filter((file) => allowedTables.has(file.tableName));
|
|
130
148
|
}
|
|
149
|
+
function requireSelectedFiles(files, tables) {
|
|
150
|
+
const selectedFiles = selectManifestFiles(files, tables);
|
|
151
|
+
if (selectedFiles.length > 0)
|
|
152
|
+
return selectedFiles;
|
|
153
|
+
throw new CliUserError("no_matching_files", `No export files matched: ${tables.join(", ")}`);
|
|
154
|
+
}
|