primitive-admin 1.1.0-alpha.85 → 1.1.0-alpha.87
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 +34 -0
- package/assets/skill/skills/primitive-platform/SKILL.md +37 -1
- package/dist/src/commands/analytics.js +17 -5
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/auth-sessions.d.ts +7 -0
- package/dist/src/commands/auth-sessions.js +144 -0
- package/dist/src/commands/auth-sessions.js.map +1 -0
- package/dist/src/commands/auth.js +47 -8
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/collections.js +648 -1
- package/dist/src/commands/collections.js.map +1 -1
- package/dist/src/commands/database-type-configs.js +1 -1
- package/dist/src/commands/database-type-configs.js.map +1 -1
- package/dist/src/commands/databases.js +11 -2
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +20 -0
- package/dist/src/commands/documents.js +213 -17
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/functions.js +56 -5
- package/dist/src/commands/functions.js.map +1 -1
- package/dist/src/commands/integrations.js +4 -0
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/sync.d.ts +83 -41
- package/dist/src/commands/sync.js +553 -81
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/users.js +3 -1
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +111 -7
- package/dist/src/lib/api-client.js +92 -20
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +1 -1
- package/dist/src/lib/app-settings-descriptor.js +0 -1
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/collection-export.d.ts +184 -0
- package/dist/src/lib/collection-export.js +252 -0
- package/dist/src/lib/collection-export.js.map +1 -0
- package/dist/src/lib/config-object-descriptor.js +17 -3
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/config-payload.d.ts +8 -1
- package/dist/src/lib/config-payload.js +29 -9
- package/dist/src/lib/config-payload.js.map +1 -1
- package/dist/src/lib/config-surface.js +11 -7
- package/dist/src/lib/config-surface.js.map +1 -1
- package/dist/src/lib/deprecation.d.ts +22 -0
- package/dist/src/lib/deprecation.js +43 -0
- package/dist/src/lib/deprecation.js.map +1 -0
- package/dist/src/lib/env-resolver-core.js +10 -3
- package/dist/src/lib/env-resolver-core.js.map +1 -1
- package/dist/src/lib/function-db-types.d.ts +14 -3
- package/dist/src/lib/function-db-types.js +55 -11
- package/dist/src/lib/function-db-types.js.map +1 -1
- package/dist/src/lib/function-document-types.d.ts +13 -2
- package/dist/src/lib/function-document-types.js +79 -30
- package/dist/src/lib/function-document-types.js.map +1 -1
- package/dist/src/lib/function-trigger-listing.d.ts +27 -0
- package/dist/src/lib/function-trigger-listing.js +70 -0
- package/dist/src/lib/function-trigger-listing.js.map +1 -0
- package/dist/src/lib/generated-config-surfaces.d.ts +540 -10
- package/dist/src/lib/generated-config-surfaces.js +1915 -173
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/log-inspection.d.ts +18 -1
- package/dist/src/lib/log-inspection.js +5 -0
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/logout-admin-session.d.ts +33 -0
- package/dist/src/lib/logout-admin-session.js +70 -0
- package/dist/src/lib/logout-admin-session.js.map +1 -0
- package/dist/src/lib/prompt-cost-format.d.ts +11 -0
- package/dist/src/lib/prompt-cost-format.js +41 -0
- package/dist/src/lib/prompt-cost-format.js.map +1 -0
- package/dist/src/lib/prompt-schema-codegen.d.ts +53 -0
- package/dist/src/lib/prompt-schema-codegen.js +253 -3
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
- package/dist/src/lib/refresh-admin-credentials.d.ts +9 -1
- package/dist/src/lib/refresh-admin-credentials.js +22 -2
- package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
- package/dist/src/lib/storage-pending-retry.d.ts +26 -0
- package/dist/src/lib/storage-pending-retry.js +42 -0
- package/dist/src/lib/storage-pending-retry.js.map +1 -0
- package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
- package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
- package/dist/src/lib/swift-codegen/banners.d.ts +6 -0
- package/dist/src/lib/swift-codegen/banners.js +6 -0
- package/dist/src/lib/swift-codegen/banners.js.map +1 -1
- package/dist/src/lib/swift-codegen/dbGenerator.js +11 -3
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/functionGenerator.d.ts +7 -0
- package/dist/src/lib/swift-codegen/functionGenerator.js +41 -3
- package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.js +8 -2
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-usage.d.ts +11 -13
- package/dist/src/lib/workflow-usage.js +12 -14
- package/dist/src/lib/workflow-usage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* One refused records call, as one line for an operator (#3764).
|
|
4
|
+
*
|
|
5
|
+
* The same composition `describeBulkFailure` makes for the atomic blob, on the
|
|
6
|
+
* verbs that have no `--json` envelope to fill: the server's sentence, the
|
|
7
|
+
* stable code a script branches on, and the status. `statusCode` FIRST —
|
|
8
|
+
* `cli/src/lib/api-client.ts`'s `ApiError` is where the status lives, and a
|
|
9
|
+
* reader that asks for `status` alone prints nothing at all (#3597 paid for
|
|
10
|
+
* that once already).
|
|
11
|
+
*/
|
|
12
|
+
export declare function describeRecordsFailure(err: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* The `--document-format` an operator stated, or the refusal (#3764, E10).
|
|
15
|
+
*
|
|
16
|
+
* Refused HERE rather than by the server, because an operator CAN act on it
|
|
17
|
+
* (principle 6) and a round trip to be told what they typed wrong is a round
|
|
18
|
+
* trip they do not need to spend. Commander hands the raw string over, so the
|
|
19
|
+
* digits are what is read; an unset flag states nothing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseDocumentFormatOption(value: unknown): 1 | 2 | undefined;
|
|
2
22
|
/**
|
|
3
23
|
* What `documents records bulk` reports when the write is refused (#3619).
|
|
4
24
|
*
|
|
@@ -35,6 +35,23 @@ function printIngestSession(session) {
|
|
|
35
35
|
printResult(label, value);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* One refused records call, as one line for an operator (#3764).
|
|
40
|
+
*
|
|
41
|
+
* The same composition `describeBulkFailure` makes for the atomic blob, on the
|
|
42
|
+
* verbs that have no `--json` envelope to fill: the server's sentence, the
|
|
43
|
+
* stable code a script branches on, and the status. `statusCode` FIRST —
|
|
44
|
+
* `cli/src/lib/api-client.ts`'s `ApiError` is where the status lives, and a
|
|
45
|
+
* reader that asks for `status` alone prints nothing at all (#3597 paid for
|
|
46
|
+
* that once already).
|
|
47
|
+
*/
|
|
48
|
+
export function describeRecordsFailure(err) {
|
|
49
|
+
const message = (typeof err?.message === "string" && err.message) || String(err);
|
|
50
|
+
const code = typeof err?.code === "string" && err.code ? err.code : undefined;
|
|
51
|
+
const raw = err?.statusCode ?? err?.status;
|
|
52
|
+
const status = Number.isFinite(Number(raw)) ? Number(raw) : undefined;
|
|
53
|
+
return `${message}${code ? ` [${code}]` : ""}${status !== undefined ? ` (${status})` : ""}`;
|
|
54
|
+
}
|
|
38
55
|
/**
|
|
39
56
|
* Report a document's format (#2816) — but only when it is a large document.
|
|
40
57
|
*
|
|
@@ -49,6 +66,30 @@ function printDocumentFormat(doc) {
|
|
|
49
66
|
keyValue("Format", "2 (large document)");
|
|
50
67
|
}
|
|
51
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The `--document-format` an operator stated, or the refusal (#3764, E10).
|
|
71
|
+
*
|
|
72
|
+
* Refused HERE rather than by the server, because an operator CAN act on it
|
|
73
|
+
* (principle 6) and a round trip to be told what they typed wrong is a round
|
|
74
|
+
* trip they do not need to spend. Commander hands the raw string over, so the
|
|
75
|
+
* digits are what is read; an unset flag states nothing.
|
|
76
|
+
*/
|
|
77
|
+
export function parseDocumentFormatOption(value) {
|
|
78
|
+
if (value === undefined || value === null)
|
|
79
|
+
return undefined;
|
|
80
|
+
const text = String(value).trim();
|
|
81
|
+
if (text === "1")
|
|
82
|
+
return 1;
|
|
83
|
+
if (text === "2")
|
|
84
|
+
return 2;
|
|
85
|
+
throw new Error(`Invalid --document-format "${String(value)}". Expected 1 (an ordinary ` +
|
|
86
|
+
`document) or 2 (a large document).`);
|
|
87
|
+
}
|
|
88
|
+
/** The `--document-format <1|2>` flag, said once for every records verb. */
|
|
89
|
+
const DOCUMENT_FORMAT_FLAG = "--document-format <1|2>";
|
|
90
|
+
const DOCUMENT_FORMAT_HELP = "The document format you expect: 1 (ordinary) or 2 (large). A disagreement " +
|
|
91
|
+
"with the platform's own answer is refused with DOCUMENT_FORMAT_MISMATCH " +
|
|
92
|
+
"rather than answered out of the wrong tables";
|
|
52
93
|
/**
|
|
53
94
|
* What `documents records bulk` reports when the write is refused (#3619).
|
|
54
95
|
*
|
|
@@ -526,7 +567,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
526
567
|
}
|
|
527
568
|
}
|
|
528
569
|
catch (err) {
|
|
529
|
-
|
|
570
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
571
|
+
// refused records call is scriptable rather than only readable.
|
|
572
|
+
error(describeRecordsFailure(err));
|
|
530
573
|
process.exit(1);
|
|
531
574
|
}
|
|
532
575
|
});
|
|
@@ -577,7 +620,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
577
620
|
]));
|
|
578
621
|
}
|
|
579
622
|
catch (err) {
|
|
580
|
-
|
|
623
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
624
|
+
// refused records call is scriptable rather than only readable.
|
|
625
|
+
error(describeRecordsFailure(err));
|
|
581
626
|
process.exit(1);
|
|
582
627
|
}
|
|
583
628
|
});
|
|
@@ -592,13 +637,24 @@ Permissions (enforced by the server, not the CLI):
|
|
|
592
637
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
593
638
|
.option("--limit <n>", "Maximum number of records to return (max 100)", parseInt)
|
|
594
639
|
.option("--cursor <cursor>", "Pagination cursor from a previous query")
|
|
640
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
595
641
|
.option("--json", "Output as JSON")
|
|
596
642
|
.action(async (documentId, modelName, options) => {
|
|
597
643
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
644
|
+
let documentFormat;
|
|
645
|
+
try {
|
|
646
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
647
|
+
}
|
|
648
|
+
catch (err) {
|
|
649
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
650
|
+
// refused records call is scriptable rather than only readable.
|
|
651
|
+
error(describeRecordsFailure(err));
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
598
654
|
const filter = parseFilterOptions(options);
|
|
599
655
|
const client = new ApiClient();
|
|
600
656
|
try {
|
|
601
|
-
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter, limit: options.limit, cursor: options.cursor });
|
|
657
|
+
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter, limit: options.limit, cursor: options.cursor, documentFormat });
|
|
602
658
|
if (options.json) {
|
|
603
659
|
json(result);
|
|
604
660
|
return;
|
|
@@ -636,7 +692,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
636
692
|
}
|
|
637
693
|
}
|
|
638
694
|
catch (err) {
|
|
639
|
-
|
|
695
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
696
|
+
// refused records call is scriptable rather than only readable.
|
|
697
|
+
error(describeRecordsFailure(err));
|
|
640
698
|
process.exit(1);
|
|
641
699
|
}
|
|
642
700
|
});
|
|
@@ -648,15 +706,26 @@ Permissions (enforced by the server, not the CLI):
|
|
|
648
706
|
.argument("<model-name>", "Model name")
|
|
649
707
|
.argument("<record-id>", "Record ID")
|
|
650
708
|
.option("--app <app-id>", "App ID")
|
|
709
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
651
710
|
.option("--json", "Output as JSON")
|
|
652
711
|
.action(async (documentId, modelName, recordId, options) => {
|
|
653
712
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
713
|
+
let documentFormat;
|
|
714
|
+
try {
|
|
715
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
716
|
+
}
|
|
717
|
+
catch (err) {
|
|
718
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
719
|
+
// refused records call is scriptable rather than only readable.
|
|
720
|
+
error(describeRecordsFailure(err));
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
654
723
|
const client = new ApiClient();
|
|
655
724
|
try {
|
|
656
725
|
// A single-record fetch is a query filtered by primary-key id (there is
|
|
657
726
|
// no dedicated get endpoint — reuse queryDocumentRecords, exactly as
|
|
658
727
|
// the `databases records get` twin reuses queryDatabaseRecords).
|
|
659
|
-
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter: { id: recordId }, limit: 1 });
|
|
728
|
+
const result = await client.queryDocumentRecords(resolvedAppId, documentId, modelName, { filter: { id: recordId }, limit: 1, documentFormat });
|
|
660
729
|
const record = result.items[0];
|
|
661
730
|
if (options.json) {
|
|
662
731
|
json(record ?? null);
|
|
@@ -680,7 +749,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
680
749
|
}
|
|
681
750
|
}
|
|
682
751
|
catch (err) {
|
|
683
|
-
|
|
752
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
753
|
+
// refused records call is scriptable rather than only readable.
|
|
754
|
+
error(describeRecordsFailure(err));
|
|
684
755
|
process.exit(1);
|
|
685
756
|
}
|
|
686
757
|
});
|
|
@@ -692,14 +763,25 @@ Permissions (enforced by the server, not the CLI):
|
|
|
692
763
|
.argument("<model-name>", "Model name to count")
|
|
693
764
|
.option("--app <app-id>", "App ID")
|
|
694
765
|
.option("--filter <json>", "Filter as JSON (e.g. '{\"status\":\"open\"}')")
|
|
766
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
695
767
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
696
768
|
.option("--json", "Output as JSON")
|
|
697
769
|
.action(async (documentId, modelName, options) => {
|
|
698
770
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
771
|
+
let documentFormat;
|
|
772
|
+
try {
|
|
773
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
777
|
+
// refused records call is scriptable rather than only readable.
|
|
778
|
+
error(describeRecordsFailure(err));
|
|
779
|
+
process.exit(1);
|
|
780
|
+
}
|
|
699
781
|
const filter = parseFilterOptions(options);
|
|
700
782
|
const client = new ApiClient();
|
|
701
783
|
try {
|
|
702
|
-
const result = await client.countDocumentRecords(resolvedAppId, documentId, modelName, { filter });
|
|
784
|
+
const result = await client.countDocumentRecords(resolvedAppId, documentId, modelName, { filter, documentFormat });
|
|
703
785
|
if (options.json) {
|
|
704
786
|
json(result);
|
|
705
787
|
return;
|
|
@@ -707,7 +789,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
707
789
|
console.log(` ${result.count}`);
|
|
708
790
|
}
|
|
709
791
|
catch (err) {
|
|
710
|
-
|
|
792
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
793
|
+
// refused records call is scriptable rather than only readable.
|
|
794
|
+
error(describeRecordsFailure(err));
|
|
711
795
|
process.exit(1);
|
|
712
796
|
}
|
|
713
797
|
});
|
|
@@ -727,9 +811,20 @@ Permissions (enforced by the server, not the CLI):
|
|
|
727
811
|
.option("--group-by <field>", "Group results by this plain field (repeatable; StringSet fields are not supported)", (value, previous = []) => previous.concat(value), [])
|
|
728
812
|
.option("--filter <json>", "Filter as JSON")
|
|
729
813
|
.option("--filter-file <path>", "Read filter from a JSON or TOML file")
|
|
814
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
730
815
|
.option("--json", "Output as JSON")
|
|
731
816
|
.action(async (documentId, modelName, options) => {
|
|
732
817
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
818
|
+
let documentFormat;
|
|
819
|
+
try {
|
|
820
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
821
|
+
}
|
|
822
|
+
catch (err) {
|
|
823
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
824
|
+
// refused records call is scriptable rather than only readable.
|
|
825
|
+
error(describeRecordsFailure(err));
|
|
826
|
+
process.exit(1);
|
|
827
|
+
}
|
|
733
828
|
if (!AGGREGATE_OPS.includes(options.op)) {
|
|
734
829
|
error(`Invalid --op "${options.op}". Expected one of: ${AGGREGATE_OPS.join(", ")}.`);
|
|
735
830
|
process.exit(1);
|
|
@@ -752,7 +847,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
752
847
|
const groupBy = options.groupBy || [];
|
|
753
848
|
const client = new ApiClient();
|
|
754
849
|
try {
|
|
755
|
-
const response = await client.aggregateDocumentRecords(resolvedAppId, documentId, modelName, { operations: [operation], groupBy, filter });
|
|
850
|
+
const response = await client.aggregateDocumentRecords(resolvedAppId, documentId, modelName, { operations: [operation], groupBy, filter }, { documentFormat });
|
|
756
851
|
if (options.json) {
|
|
757
852
|
json(response);
|
|
758
853
|
return;
|
|
@@ -789,7 +884,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
789
884
|
}
|
|
790
885
|
}
|
|
791
886
|
catch (err) {
|
|
792
|
-
|
|
887
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
888
|
+
// refused records call is scriptable rather than only readable.
|
|
889
|
+
error(describeRecordsFailure(err));
|
|
793
890
|
process.exit(1);
|
|
794
891
|
}
|
|
795
892
|
});
|
|
@@ -812,15 +909,26 @@ Permissions (enforced by the server, not the CLI):
|
|
|
812
909
|
.option("--data <json>", "Record fields as JSON (e.g. '{\"qty\":10}')")
|
|
813
910
|
.option("--data-file <file>", "Read record fields from a JSON file")
|
|
814
911
|
.option("--upsert-on <field>", "Update the record whose <field> matches instead of creating")
|
|
912
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
815
913
|
.option("--json", "Output as JSON")
|
|
816
914
|
.action(async (documentId, modelName, options) => {
|
|
817
915
|
const data = parseDataOption(options, "record fields");
|
|
818
916
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
917
|
+
let documentFormat;
|
|
918
|
+
try {
|
|
919
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
920
|
+
}
|
|
921
|
+
catch (err) {
|
|
922
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
923
|
+
// refused records call is scriptable rather than only readable.
|
|
924
|
+
error(describeRecordsFailure(err));
|
|
925
|
+
process.exit(1);
|
|
926
|
+
}
|
|
819
927
|
const recordId = options.id || ulid();
|
|
820
928
|
const writeOptions = options.upsertOn ? { upsertOn: options.upsertOn } : undefined;
|
|
821
929
|
const client = new ApiClient();
|
|
822
930
|
try {
|
|
823
|
-
const result = await client.saveDocumentRecord(resolvedAppId, documentId, modelName, { id: recordId, data, ...(writeOptions ? { options: writeOptions } : {}) });
|
|
931
|
+
const result = await client.saveDocumentRecord(resolvedAppId, documentId, modelName, { id: recordId, data, ...(writeOptions ? { options: writeOptions } : {}) }, { documentFormat });
|
|
824
932
|
if (options.json) {
|
|
825
933
|
json(result);
|
|
826
934
|
return;
|
|
@@ -828,7 +936,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
828
936
|
success(`Record saved: ${result.record?.id ?? recordId}`);
|
|
829
937
|
}
|
|
830
938
|
catch (err) {
|
|
831
|
-
|
|
939
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
940
|
+
// refused records call is scriptable rather than only readable.
|
|
941
|
+
error(describeRecordsFailure(err));
|
|
832
942
|
process.exit(1);
|
|
833
943
|
}
|
|
834
944
|
});
|
|
@@ -841,14 +951,25 @@ Permissions (enforced by the server, not the CLI):
|
|
|
841
951
|
.argument("<record-id>", "Record ID to patch")
|
|
842
952
|
.option("--app <app-id>", "App ID")
|
|
843
953
|
.option("--data <json>", "Fields to merge as JSON (e.g. '{\"status\":\"closed\"}')")
|
|
954
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
844
955
|
.option("--data-file <file>", "Read fields from a JSON file")
|
|
845
956
|
.option("--json", "Output as JSON")
|
|
846
957
|
.action(async (documentId, modelName, recordId, options) => {
|
|
847
958
|
const data = parseDataOption(options, "fields to merge");
|
|
848
959
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
960
|
+
let documentFormat;
|
|
961
|
+
try {
|
|
962
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
963
|
+
}
|
|
964
|
+
catch (err) {
|
|
965
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
966
|
+
// refused records call is scriptable rather than only readable.
|
|
967
|
+
error(describeRecordsFailure(err));
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
849
970
|
const client = new ApiClient();
|
|
850
971
|
try {
|
|
851
|
-
const result = await client.patchDocumentRecord(resolvedAppId, documentId, modelName, recordId, { data });
|
|
972
|
+
const result = await client.patchDocumentRecord(resolvedAppId, documentId, modelName, recordId, { data }, { documentFormat });
|
|
852
973
|
if (options.json) {
|
|
853
974
|
json(result);
|
|
854
975
|
return;
|
|
@@ -856,7 +977,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
856
977
|
success(`Record patched: ${recordId}`);
|
|
857
978
|
}
|
|
858
979
|
catch (err) {
|
|
859
|
-
|
|
980
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
981
|
+
// refused records call is scriptable rather than only readable.
|
|
982
|
+
error(describeRecordsFailure(err));
|
|
860
983
|
process.exit(1);
|
|
861
984
|
}
|
|
862
985
|
});
|
|
@@ -868,10 +991,21 @@ Permissions (enforced by the server, not the CLI):
|
|
|
868
991
|
.argument("<model-name>", "Model name")
|
|
869
992
|
.argument("<record-id>", "Record ID to delete")
|
|
870
993
|
.option("--app <app-id>", "App ID")
|
|
994
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
871
995
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
872
996
|
.option("--json", "Output as JSON")
|
|
873
997
|
.action(async (documentId, modelName, recordId, options) => {
|
|
874
998
|
const resolvedAppId = resolveAppId(undefined, options);
|
|
999
|
+
let documentFormat;
|
|
1000
|
+
try {
|
|
1001
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1005
|
+
// refused records call is scriptable rather than only readable.
|
|
1006
|
+
error(describeRecordsFailure(err));
|
|
1007
|
+
process.exit(1);
|
|
1008
|
+
}
|
|
875
1009
|
if (!options.yes) {
|
|
876
1010
|
const confirmed = await confirmPrompt(`Delete record ${recordId} from ${modelName} in document ${documentId}?`);
|
|
877
1011
|
if (!confirmed) {
|
|
@@ -881,7 +1015,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
881
1015
|
}
|
|
882
1016
|
const client = new ApiClient();
|
|
883
1017
|
try {
|
|
884
|
-
const result = await client.deleteDocumentRecord(resolvedAppId, documentId, modelName, recordId);
|
|
1018
|
+
const result = await client.deleteDocumentRecord(resolvedAppId, documentId, modelName, recordId, { documentFormat });
|
|
885
1019
|
if (options.json) {
|
|
886
1020
|
json(result);
|
|
887
1021
|
return;
|
|
@@ -891,7 +1025,9 @@ Permissions (enforced by the server, not the CLI):
|
|
|
891
1025
|
success(`Record deleted: ${recordId}`);
|
|
892
1026
|
}
|
|
893
1027
|
catch (err) {
|
|
894
|
-
|
|
1028
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1029
|
+
// refused records call is scriptable rather than only readable.
|
|
1030
|
+
error(describeRecordsFailure(err));
|
|
895
1031
|
process.exit(1);
|
|
896
1032
|
}
|
|
897
1033
|
});
|
|
@@ -906,9 +1042,20 @@ Permissions (enforced by the server, not the CLI):
|
|
|
906
1042
|
"create/patch, not allowed on delete; a create `id` must be a 26-char " +
|
|
907
1043
|
"uppercase Crockford ULID")
|
|
908
1044
|
.option("--app <app-id>", "App ID")
|
|
1045
|
+
.option(DOCUMENT_FORMAT_FLAG, DOCUMENT_FORMAT_HELP)
|
|
909
1046
|
.option("-y, --yes", "Skip confirmation prompt")
|
|
910
1047
|
.option("--json", "Output as JSON")
|
|
911
1048
|
.action(async (documentId, options) => {
|
|
1049
|
+
let documentFormat;
|
|
1050
|
+
try {
|
|
1051
|
+
documentFormat = parseDocumentFormatOption(options.documentFormat);
|
|
1052
|
+
}
|
|
1053
|
+
catch (err) {
|
|
1054
|
+
// #3764 — the server's sentence, the stable code and the status, so a
|
|
1055
|
+
// refused records call is scriptable rather than only readable.
|
|
1056
|
+
error(describeRecordsFailure(err));
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
912
1059
|
const parsed = parseDataOption(options, "operations blob", {
|
|
913
1060
|
allowArray: true,
|
|
914
1061
|
});
|
|
@@ -927,7 +1074,7 @@ Permissions (enforced by the server, not the CLI):
|
|
|
927
1074
|
}
|
|
928
1075
|
const client = new ApiClient();
|
|
929
1076
|
try {
|
|
930
|
-
const result = await client.bulkDocumentRecords(resolvedAppId, documentId, operations);
|
|
1077
|
+
const result = await client.bulkDocumentRecords(resolvedAppId, documentId, operations, { documentFormat });
|
|
931
1078
|
if (options.json) {
|
|
932
1079
|
json(result);
|
|
933
1080
|
return;
|
|
@@ -1601,6 +1748,55 @@ Permissions (enforced by the server, not the CLI):
|
|
|
1601
1748
|
process.exit(1);
|
|
1602
1749
|
}
|
|
1603
1750
|
});
|
|
1751
|
+
// ---- Effective access for a named user (#3658) ----
|
|
1752
|
+
const access = documents
|
|
1753
|
+
.command("access")
|
|
1754
|
+
.description("Read a user's effective access to a document");
|
|
1755
|
+
access
|
|
1756
|
+
.command("get")
|
|
1757
|
+
.description("Show what a user may do with a document, across direct, group, " +
|
|
1758
|
+
"collection and link access")
|
|
1759
|
+
.argument("<document-id>", "Document ID")
|
|
1760
|
+
.requiredOption("--user <user-id>", "The user whose access to resolve")
|
|
1761
|
+
.option("--app <app-id>", "App ID")
|
|
1762
|
+
.option("--json", "Output as JSON")
|
|
1763
|
+
.action(async (documentId, options) => {
|
|
1764
|
+
const resolvedAppId = resolveAppId(undefined, options);
|
|
1765
|
+
const client = new ApiClient();
|
|
1766
|
+
try {
|
|
1767
|
+
const result = await client.validateDocumentAccessForUser(resolvedAppId, documentId, options.user);
|
|
1768
|
+
const row = {
|
|
1769
|
+
documentId,
|
|
1770
|
+
userId: options.user,
|
|
1771
|
+
hasAccess: result?.hasAccess === true,
|
|
1772
|
+
permission: result?.permission ?? null,
|
|
1773
|
+
accessSource: result?.accessSource ?? null,
|
|
1774
|
+
appRole: result?.appRole ?? null,
|
|
1775
|
+
};
|
|
1776
|
+
if (options.json) {
|
|
1777
|
+
json(row);
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
console.log(formatTable([row], [
|
|
1781
|
+
{ header: "DOCUMENT", key: "documentId" },
|
|
1782
|
+
{ header: "USER", key: "userId" },
|
|
1783
|
+
{ header: "ACCESS", key: "hasAccess" },
|
|
1784
|
+
{ header: "PERMISSION", key: "permission" },
|
|
1785
|
+
{ header: "SOURCE", key: "accessSource" },
|
|
1786
|
+
{ header: "APP_ROLE", key: "appRole" },
|
|
1787
|
+
]));
|
|
1788
|
+
if (!row.hasAccess) {
|
|
1789
|
+
// The role is not a grant: only the tag routes admit an
|
|
1790
|
+
// administrator without one, so an operator reading "admin" here
|
|
1791
|
+
// should not conclude the user may write content.
|
|
1792
|
+
info("No document grant. An app role admits tag changes only, never a content write.");
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
catch (err) {
|
|
1796
|
+
error(err.message);
|
|
1797
|
+
process.exit(1);
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1604
1800
|
// ---- Link access ("anyone with the link") ----
|
|
1605
1801
|
const linkAccess = documents
|
|
1606
1802
|
.command("link-access")
|