@versionone/agility-sdk 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +135 -0
- package/README.md +24 -11
- package/dist/index.cjs +317 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4201 -3851
- package/dist/index.d.ts +4201 -3851
- package/dist/index.js +311 -80
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -35,19 +35,25 @@ __export(index_exports, {
|
|
|
35
35
|
TypeCache: () => TypeCache,
|
|
36
36
|
UNGUARDABLE_TYPES: () => UNGUARDABLE_TYPES,
|
|
37
37
|
UnguardableError: () => UnguardableError,
|
|
38
|
+
UsageError: () => UsageError,
|
|
39
|
+
WrappedError: () => WrappedError,
|
|
38
40
|
assetApi: () => assetApi_exports,
|
|
39
41
|
assetStateOf: () => assetStateOf,
|
|
40
42
|
attachment: () => attachment_exports,
|
|
41
43
|
createFetchTransport: () => createFetchTransport,
|
|
42
44
|
describeBatch: () => describe,
|
|
45
|
+
fields: () => fields_exports,
|
|
43
46
|
guard: () => guard,
|
|
47
|
+
importantName: () => importantName,
|
|
44
48
|
isAssetNumber: () => isAssetNumber,
|
|
45
49
|
isOidToken: () => isOidToken,
|
|
50
|
+
kindOf: () => kindOf,
|
|
46
51
|
loc2: () => loc2_exports,
|
|
47
52
|
meta: () => meta_exports,
|
|
48
53
|
momentless: () => momentless,
|
|
49
54
|
namesOf: () => namesOf,
|
|
50
55
|
parseOid: () => parseOid,
|
|
56
|
+
parseViolation: () => parseViolation,
|
|
51
57
|
queryV1: () => queryV1_exports,
|
|
52
58
|
resolveNumbers: () => resolveNumbers,
|
|
53
59
|
resolveRef: () => resolveRef,
|
|
@@ -67,16 +73,61 @@ __export(index_exports, {
|
|
|
67
73
|
module.exports = __toCommonJS(index_exports);
|
|
68
74
|
|
|
69
75
|
// src/core/errors.ts
|
|
76
|
+
function kindOf(status, code) {
|
|
77
|
+
switch (code) {
|
|
78
|
+
case "ABORTED":
|
|
79
|
+
return "aborted";
|
|
80
|
+
case "TIMEOUT":
|
|
81
|
+
case "NETWORK_ERROR":
|
|
82
|
+
return "transient";
|
|
83
|
+
case "BAD_JSON":
|
|
84
|
+
case "BAD_RESPONSE":
|
|
85
|
+
return "protocol";
|
|
86
|
+
case "USAGE":
|
|
87
|
+
return "usage";
|
|
88
|
+
case "CommandFailure":
|
|
89
|
+
return "commandFailure";
|
|
90
|
+
case "QUERY_REJECTED":
|
|
91
|
+
case "TOO_LARGE":
|
|
92
|
+
return "invalid";
|
|
93
|
+
case "NOT_FOUND":
|
|
94
|
+
return "notFound";
|
|
95
|
+
}
|
|
96
|
+
if (status === 401) return "auth";
|
|
97
|
+
if (status === 403) return "forbidden";
|
|
98
|
+
if (status === 404) return "notFound";
|
|
99
|
+
if (status === 408 || status === 429 || status >= 500) return "transient";
|
|
100
|
+
if (status >= 400) return "invalid";
|
|
101
|
+
return "unknown";
|
|
102
|
+
}
|
|
103
|
+
var causeOf = (details) => details && typeof details === "object" && "cause" in details ? details.cause : void 0;
|
|
70
104
|
var ApiError = class extends Error {
|
|
105
|
+
/** The HTTP status; 0 when no answer came back (network, timeout, abort, usage). */
|
|
71
106
|
status;
|
|
72
107
|
code;
|
|
73
108
|
details;
|
|
74
|
-
|
|
75
|
-
|
|
109
|
+
kind;
|
|
110
|
+
constructor(message, status, code, details, kind = kindOf(status, code)) {
|
|
111
|
+
const cause = causeOf(details);
|
|
112
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
76
113
|
this.name = "ApiError";
|
|
77
114
|
this.status = status;
|
|
78
115
|
this.code = code;
|
|
79
116
|
this.details = details;
|
|
117
|
+
this.kind = kind;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var UsageError = class extends ApiError {
|
|
121
|
+
constructor(message, details) {
|
|
122
|
+
super(message, 0, "USAGE", details, "usage");
|
|
123
|
+
this.name = "UsageError";
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var WrappedError = class extends ApiError {
|
|
127
|
+
constructor(message, code, cause, details) {
|
|
128
|
+
const inner = cause instanceof ApiError ? cause : void 0;
|
|
129
|
+
super(message, inner?.status ?? 0, code, { ...details, cause }, inner?.kind ?? "unknown");
|
|
130
|
+
this.name = "WrappedError";
|
|
80
131
|
}
|
|
81
132
|
};
|
|
82
133
|
|
|
@@ -216,7 +267,7 @@ function isOidToken(value) {
|
|
|
216
267
|
}
|
|
217
268
|
function parseOid(token) {
|
|
218
269
|
const m = OID_TOKEN.exec(token);
|
|
219
|
-
if (!m) throw new
|
|
270
|
+
if (!m) throw new UsageError(`"${token}" is not an OID token (Story:1234)`);
|
|
220
271
|
return { type: m[1], id: Number(m[2]), moment: m[3] === void 0 ? null : Number(m[3]) };
|
|
221
272
|
}
|
|
222
273
|
function momentless(token) {
|
|
@@ -240,6 +291,7 @@ __export(assetApi_exports, {
|
|
|
240
291
|
mergeOutcome: () => mergeOutcome,
|
|
241
292
|
momentless: () => momentless,
|
|
242
293
|
outcomeOf: () => outcomeOf,
|
|
294
|
+
parseViolation: () => parseViolation,
|
|
243
295
|
preview: () => preview,
|
|
244
296
|
queries: () => queries,
|
|
245
297
|
query: () => query,
|
|
@@ -405,15 +457,25 @@ function toNumber(value) {
|
|
|
405
457
|
const n = typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
406
458
|
return Number.isNaN(n) ? value : n;
|
|
407
459
|
}
|
|
460
|
+
function parseViolation(message) {
|
|
461
|
+
const parts = message.trim().split("'");
|
|
462
|
+
if (parts[0] !== "Violation" || !parts[1]) return null;
|
|
463
|
+
const rule = parts[1], rest = parts.slice(2).filter((p) => p !== "");
|
|
464
|
+
if (rest[0] === "AttributeDefinition" && rest[1] && rest[2]) return { rule, attribute: `${rest[2]}.${rest[1]}`, tag: rest.join("'") };
|
|
465
|
+
const token = rest.length === 1 && /^[A-Za-z][A-Za-z0-9_]*\.[A-Za-z][A-Za-z0-9_]*$/.test(rest[0]) ? rest[0] : null;
|
|
466
|
+
return { rule, attribute: token, tag: !token && rest.length > 1 ? rest.join("'") : null };
|
|
467
|
+
}
|
|
408
468
|
function failuresOf(response) {
|
|
409
469
|
return (response.commandFailures?.commands ?? []).map(({ error, ...echoed }) => ({
|
|
410
470
|
index: typeof error === "object" && error && typeof error.sourceCommandIndex === "number" ? error.sourceCommandIndex : null,
|
|
411
471
|
message: typeof error === "string" ? error : error?.message || "command failed",
|
|
412
|
-
echoed
|
|
472
|
+
echoed,
|
|
473
|
+
violation: parseViolation(typeof error === "string" ? error : error?.message || "")
|
|
413
474
|
}));
|
|
414
475
|
}
|
|
415
476
|
function outcomeOf(response) {
|
|
416
477
|
return {
|
|
478
|
+
requestId: typeof response.requestId === "string" && response.requestId ? response.requestId : null,
|
|
417
479
|
created: (response.assetsCreated?.oidTokens ?? []).map(momentless),
|
|
418
480
|
modified: (response.assetsModified?.oidTokens ?? []).map(momentless),
|
|
419
481
|
operated: (response.assetsOperatedOn?.oidTokens ?? []).map(momentless),
|
|
@@ -422,24 +484,28 @@ function outcomeOf(response) {
|
|
|
422
484
|
durationSeconds: typeof response.durationSeconds === "number" ? response.durationSeconds : null
|
|
423
485
|
};
|
|
424
486
|
}
|
|
425
|
-
var emptyOutcome = () => ({ created: [], modified: [], operated: [], failures: [], queryResults: [], durationSeconds: null });
|
|
487
|
+
var emptyOutcome = () => ({ requestId: null, created: [], modified: [], operated: [], failures: [], queryResults: [], durationSeconds: null });
|
|
426
488
|
function mergeOutcome(into, done, offset) {
|
|
427
489
|
into.created.push(...done.created);
|
|
428
490
|
into.modified.push(...done.modified);
|
|
429
491
|
into.operated.push(...done.operated);
|
|
430
492
|
into.failures.push(...done.failures.map((f) => ({ ...f, index: f.index === null ? null : f.index + offset })));
|
|
431
493
|
into.queryResults.push(...done.queryResults);
|
|
494
|
+
into.requestId = null;
|
|
432
495
|
if (done.durationSeconds !== null) into.durationSeconds = (into.durationSeconds ?? 0) + done.durationSeconds;
|
|
433
496
|
return into;
|
|
434
497
|
}
|
|
435
498
|
var CommandFailureError = class extends ApiError {
|
|
436
499
|
failures;
|
|
437
500
|
response;
|
|
501
|
+
/** What the batch did, in the model's terms: the commands that succeeded are committed. */
|
|
502
|
+
outcome;
|
|
438
503
|
constructor(response, failures = failuresOf(response)) {
|
|
439
504
|
super(failures.map((f) => f.message).join("; ") || "command failed", 200, "CommandFailure", failures);
|
|
440
505
|
this.name = "CommandFailureError";
|
|
441
506
|
this.failures = failures;
|
|
442
507
|
this.response = response;
|
|
508
|
+
this.outcome = outcomeOf(response);
|
|
443
509
|
}
|
|
444
510
|
};
|
|
445
511
|
var bodyOf = (c) => {
|
|
@@ -475,7 +541,7 @@ async function send(transport, commands, options = {}) {
|
|
|
475
541
|
const results = response.queryResult?.results ?? [];
|
|
476
542
|
let aligned = realign(queries3, results, failed);
|
|
477
543
|
if (!aligned) {
|
|
478
|
-
if (!failures.some((f) => f.index === null)) throw new
|
|
544
|
+
if (!failures.some((f) => f.index === null)) throw new ApiError(`api/asset returned ${results.length} query results for ${queries3.filter((q) => !failed.has(q.index)).length} queries that did not fail; cannot tell which result belongs to which query`, 200, "BAD_RESPONSE", response);
|
|
479
545
|
aligned = queries3.map(() => null);
|
|
480
546
|
}
|
|
481
547
|
const contextOf = (i) => {
|
|
@@ -497,7 +563,7 @@ async function query(transport, q, options = {}) {
|
|
|
497
563
|
return rows ?? [];
|
|
498
564
|
}
|
|
499
565
|
async function queryPage(transport, q, options = {}) {
|
|
500
|
-
if (!q.page) throw new
|
|
566
|
+
if (!q.page) throw new UsageError(`query of ${q.from} without a page: every api/asset read must be bounded`);
|
|
501
567
|
const response = await send(transport, [{ ...q, needTotal: true }], { ...options, throwOnFailure: true });
|
|
502
568
|
const rows = response.queryResult.results[0] ?? [];
|
|
503
569
|
const reported = response.queryResult.resultMetaData?.[0]?.total;
|
|
@@ -505,7 +571,7 @@ async function queryPage(transport, q, options = {}) {
|
|
|
505
571
|
return { rows, total, more: total === null ? rows.length === q.page.size : q.page.start + rows.length < total };
|
|
506
572
|
}
|
|
507
573
|
async function queries(transport, qs, options = {}) {
|
|
508
|
-
for (const q of qs) if (!q.page) throw new
|
|
574
|
+
for (const q of qs) if (!q.page) throw new UsageError(`query of ${q.from} without a page: every api/asset read must be bounded`);
|
|
509
575
|
const response = await send(transport, qs, { ...options, throwOnFailure: true });
|
|
510
576
|
return qs.map((_, i) => response.queryResult.results[i] ?? []);
|
|
511
577
|
}
|
|
@@ -543,7 +609,7 @@ function fromBase64(text) {
|
|
|
543
609
|
}
|
|
544
610
|
function numericId(spec) {
|
|
545
611
|
const m = /^(?:[A-Za-z]+:)?(\d+)(?::\d+)?$/.exec(String(spec).trim());
|
|
546
|
-
if (!m) throw new
|
|
612
|
+
if (!m) throw new UsageError(`"${spec}" is not an asset id (5678, or an OID token such as Attachment:5678)`);
|
|
547
613
|
return m[1];
|
|
548
614
|
}
|
|
549
615
|
var join = (baseUrl, path) => `${baseUrl.replace(/\/+$/, "")}/${path}`;
|
|
@@ -639,9 +705,9 @@ async function get(transport, id) {
|
|
|
639
705
|
const file = await getFile(transport, meta.id);
|
|
640
706
|
return { ...meta, bytes: file.bytes, contentType: file.contentType ?? meta.contentType, recordedContentType: meta.contentType };
|
|
641
707
|
}
|
|
642
|
-
var AttachmentUploadError = class extends
|
|
708
|
+
var AttachmentUploadError = class extends WrappedError {
|
|
643
709
|
constructor(attachment, cause) {
|
|
644
|
-
super(`${attachment.oid} was created on its asset with no content, and uploading the file failed: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
710
|
+
super(`${attachment.oid} was created on its asset with no content, and uploading the file failed: ${cause instanceof Error ? cause.message : String(cause)}`, "ATTACHMENT_UPLOAD", cause);
|
|
645
711
|
this.attachment = attachment;
|
|
646
712
|
this.name = "AttachmentUploadError";
|
|
647
713
|
}
|
|
@@ -674,7 +740,7 @@ async function remove(transport, id) {
|
|
|
674
740
|
// src/services/guard.ts
|
|
675
741
|
var CHUNK = 100;
|
|
676
742
|
var UNGUARDABLE_TYPES = ["Idea", "IdeaChangeSet", "IdeaStatus", "IdeaComment", "IdeaCategory", "IdeaForum", "IdeaUser"];
|
|
677
|
-
var UnguardableError = class extends
|
|
743
|
+
var UnguardableError = class extends UsageError {
|
|
678
744
|
reasons;
|
|
679
745
|
constructor(reasons) {
|
|
680
746
|
super(`cannot guard this batch: ${reasons.map((r) => `command ${r.index}: ${r.reason}`).join("; ")}`);
|
|
@@ -740,7 +806,7 @@ function guard(projected, stamps) {
|
|
|
740
806
|
unstamped.push(x.oid);
|
|
741
807
|
continue;
|
|
742
808
|
}
|
|
743
|
-
if (!STAMP.test(stamp)) throw new
|
|
809
|
+
if (!STAMP.test(stamp)) throw new ApiError(`${x.oid}: ChangeDate ${JSON.stringify(stamp)} is not a timestamp; refusing to put it in a filter`, 200, "BAD_RESPONSE", { oid: x.oid, stamp });
|
|
744
810
|
const scope = { from: typeOfOid(x.oid), filter: ["ID=$oid", `ChangeDate='${stamp}'`], with: { $oid: x.oid } };
|
|
745
811
|
commands.push(x.update ? { ...scope, update: x.update } : { ...scope, execute: x.execute });
|
|
746
812
|
}
|
|
@@ -757,16 +823,14 @@ function passesOf(projected) {
|
|
|
757
823
|
}
|
|
758
824
|
return passes;
|
|
759
825
|
}
|
|
760
|
-
var GuardedPassError = class extends
|
|
826
|
+
var GuardedPassError = class extends WrappedError {
|
|
761
827
|
constructor(cause, outcome, sent, pass) {
|
|
762
|
-
super(`guarded send failed in pass ${pass + 1}${pass ? " (earlier passes applied)" : ""}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
763
|
-
this.cause = cause;
|
|
828
|
+
super(`guarded send failed in pass ${pass + 1}${pass ? " (earlier passes applied)" : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, "GUARDED_PASS", cause);
|
|
764
829
|
this.outcome = outcome;
|
|
765
830
|
this.sent = sent;
|
|
766
831
|
this.pass = pass;
|
|
767
832
|
this.name = "GuardedPassError";
|
|
768
833
|
}
|
|
769
|
-
cause;
|
|
770
834
|
outcome;
|
|
771
835
|
sent;
|
|
772
836
|
pass;
|
|
@@ -983,18 +1047,18 @@ function reverseOf(described, before, done, context = {}) {
|
|
|
983
1047
|
if (commands.length) notes.unshift("restores the state recorded at snapshotAt; anything that changed between then and sentAt is not reflected, and a scalar restored to null is cleared, not unset");
|
|
984
1048
|
return { commands, notes };
|
|
985
1049
|
}
|
|
986
|
-
var BatchRecordError = class extends
|
|
1050
|
+
var BatchRecordError = class extends WrappedError {
|
|
987
1051
|
receipt;
|
|
988
1052
|
constructor(cause, receipt) {
|
|
989
|
-
super(`the batch was sent and its outcome recorded, but reading the assets back afterwards failed, so receipt.after is incomplete: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
1053
|
+
super(`the batch was sent and its outcome recorded, but reading the assets back afterwards failed, so receipt.after is incomplete: ${cause instanceof Error ? cause.message : String(cause)}`, "BATCH_RECORD", cause);
|
|
990
1054
|
this.name = "BatchRecordError";
|
|
991
1055
|
this.receipt = receipt;
|
|
992
1056
|
}
|
|
993
1057
|
};
|
|
994
|
-
var BatchSendError = class extends
|
|
1058
|
+
var BatchSendError = class extends WrappedError {
|
|
995
1059
|
receipt;
|
|
996
1060
|
constructor(cause, receipt) {
|
|
997
|
-
super(`the batch send failed; whether the server applied it is unknown: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
1061
|
+
super(`the batch send failed; whether the server applied it is unknown: ${cause instanceof Error ? cause.message : String(cause)}`, "BATCH_SEND", cause);
|
|
998
1062
|
this.name = "BatchSendError";
|
|
999
1063
|
this.receipt = receipt;
|
|
1000
1064
|
}
|
|
@@ -1002,7 +1066,7 @@ var BatchSendError = class extends Error {
|
|
|
1002
1066
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1003
1067
|
async function runBatch(transport, schema, commands, options = {}) {
|
|
1004
1068
|
const useGuard = options.guard ?? true;
|
|
1005
|
-
if (!commands.length) throw new
|
|
1069
|
+
if (!commands.length) throw new UsageError("the batch is empty");
|
|
1006
1070
|
const refused = unguardable(commands);
|
|
1007
1071
|
const fatal = refused.filter((r) => r.reason.includes("rejected by the server"));
|
|
1008
1072
|
if (fatal.length || useGuard && refused.length) throw new UnguardableError(useGuard ? refused : fatal);
|
|
@@ -1108,6 +1172,65 @@ async function reversible(transport, receipt, options = {}) {
|
|
|
1108
1172
|
return { commands, drifted, reactivated };
|
|
1109
1173
|
}
|
|
1110
1174
|
|
|
1175
|
+
// src/wire/fields.ts
|
|
1176
|
+
var fields_exports = {};
|
|
1177
|
+
__export(fields_exports, {
|
|
1178
|
+
visibleFields: () => visibleFields,
|
|
1179
|
+
visibleFieldsByAsset: () => visibleFieldsByAsset
|
|
1180
|
+
});
|
|
1181
|
+
async function visibleFields(transport, types, scopes) {
|
|
1182
|
+
if (!types.length) return {};
|
|
1183
|
+
const params = [...scopes.map((s) => `scopeOid=${encodeURIComponent(momentless(s))}`), ...types.map((t) => `assetTypeToken=${encodeURIComponent(t)}`)].join("&");
|
|
1184
|
+
const answer = await transport.request({ method: "GET", path: `/api/visibleFields?${params}`, responseType: "json" });
|
|
1185
|
+
if (!Array.isArray(answer)) throw new ApiError("api/visibleFields did not answer a list", 200, "BAD_RESPONSE", answer);
|
|
1186
|
+
const out = {};
|
|
1187
|
+
for (const entry of answer) if (entry && typeof entry.assetType === "string") out[entry.assetType] = Array.isArray(entry.fields) ? entry.fields.filter((f) => typeof f === "string") : [];
|
|
1188
|
+
return out;
|
|
1189
|
+
}
|
|
1190
|
+
async function visibleFieldsByAsset(transport, oid) {
|
|
1191
|
+
const answer = await transport.request({ method: "GET", path: "/api/visibleFieldsByAsset", query: { oidToken: momentless(oid) }, responseType: "json" });
|
|
1192
|
+
if (!Array.isArray(answer)) throw new ApiError("api/visibleFieldsByAsset did not answer a list", 200, "BAD_RESPONSE", answer);
|
|
1193
|
+
return answer.filter((f) => typeof f === "string");
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// src/services/fields.ts
|
|
1197
|
+
async function lineage(schema, type) {
|
|
1198
|
+
const out = [];
|
|
1199
|
+
for (let t = type; t && !out.includes(t); ) {
|
|
1200
|
+
out.push(t);
|
|
1201
|
+
const def = await schema.load(t);
|
|
1202
|
+
t = def?.Base;
|
|
1203
|
+
}
|
|
1204
|
+
return out;
|
|
1205
|
+
}
|
|
1206
|
+
function importantName(token, types) {
|
|
1207
|
+
const dot = token.indexOf(".");
|
|
1208
|
+
if (dot < 0) return token;
|
|
1209
|
+
return types.includes(token.slice(0, dot)) ? token.slice(dot + 1) : null;
|
|
1210
|
+
}
|
|
1211
|
+
async function describe2(transport, schema, type, scope) {
|
|
1212
|
+
const scopeOid = momentless(scope);
|
|
1213
|
+
const types = await lineage(schema, type);
|
|
1214
|
+
const definition = await schema.load(type);
|
|
1215
|
+
const required = definition ? Object.values(definition.Attributes).filter((a) => a.IsRequired && !a.IsReadOnly).map((a) => a.Name).sort() : [];
|
|
1216
|
+
const [visible, [row]] = await Promise.all([
|
|
1217
|
+
visibleFields(transport, [type], [scopeOid]),
|
|
1218
|
+
query(transport, { from: "Scope", select: [{ from: "Scheme", select: ["ImportantFields", "IsRequiredAtClose"] }], filter: ["ID=$scope"], with: { $scope: scopeOid }, page: { size: 1, start: 0 } }, { lookup: schema })
|
|
1219
|
+
]);
|
|
1220
|
+
const scheme = row?.Scheme ?? null;
|
|
1221
|
+
const tokens = Array.isArray(scheme?.ImportantFields) ? scheme.ImportantFields.filter((t) => typeof t === "string") : [];
|
|
1222
|
+
const important = [...new Set(tokens.map((t) => importantName(t, types)).filter((n) => !!n))].sort();
|
|
1223
|
+
return {
|
|
1224
|
+
type,
|
|
1225
|
+
scope: scopeOid,
|
|
1226
|
+
scheme: scheme ? momentless(scheme._oid) : null,
|
|
1227
|
+
required,
|
|
1228
|
+
visible: visible[type] ?? [],
|
|
1229
|
+
important,
|
|
1230
|
+
requiredAtClose: scheme?.IsRequiredAtClose === true
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1111
1234
|
// src/types/asset.ts
|
|
1112
1235
|
var ASSET_STATE_CODES = { Future: 0, Active: 64, Closed: 128, Dead: 192, Template: 200, OldBrokenDownStory: 208, Deprecated: 254, Deleted: 255 };
|
|
1113
1236
|
var STATE_OF_CODE = Object.fromEntries(Object.entries(ASSET_STATE_CODES).map(([name, code]) => [code, name]));
|
|
@@ -1173,7 +1296,7 @@ async function resolveNumbers(transport, numbers, options = {}) {
|
|
|
1173
1296
|
for (const n of numbers) if (!firsts.has(n.trim().toUpperCase())) firsts.set(n.trim().toUpperCase(), n.trim());
|
|
1174
1297
|
const asked = [...firsts.values()];
|
|
1175
1298
|
const invalid = asked.filter((n) => !isAssetNumber(n));
|
|
1176
|
-
if (invalid.length) throw new
|
|
1299
|
+
if (invalid.length) throw new UsageError(`not asset numbers (expected a prefix and digits, S-1234): ${invalid.join(", ")}`);
|
|
1177
1300
|
const byNumber = /* @__PURE__ */ new Map();
|
|
1178
1301
|
for (let i = 0; i < asked.length; i += CHUNK3) {
|
|
1179
1302
|
for (const r of await askRoots(transport, asked.slice(i, i + CHUNK3), options.types)) {
|
|
@@ -1194,7 +1317,7 @@ async function resolveRef(transport, spec, options = {}) {
|
|
|
1194
1317
|
return { number: null, oid, type: typeOfOid(oid), name: null, assetState: null };
|
|
1195
1318
|
}
|
|
1196
1319
|
if (isAssetNumber(text)) return (await resolveNumbers(transport, [text], options)).found[0] ?? null;
|
|
1197
|
-
throw new
|
|
1320
|
+
throw new UsageError(`"${spec}" is neither an asset number (S-1234) nor an OID token (Story:1234)`);
|
|
1198
1321
|
}
|
|
1199
1322
|
|
|
1200
1323
|
// src/wire/meta.ts
|
|
@@ -1256,7 +1379,7 @@ async function getAssetType(transport, token) {
|
|
|
1256
1379
|
}
|
|
1257
1380
|
async function getAttributeDefinition(transport, tokenref) {
|
|
1258
1381
|
const [type, attribute] = tokenref.split(".");
|
|
1259
|
-
if (!type || !attribute) throw new
|
|
1382
|
+
if (!type || !attribute) throw new UsageError(`"${tokenref}" is not an attribute token ("Story.Name")`);
|
|
1260
1383
|
return toAttributeDefinition(await transport.request({ method: "GET", path: `/meta.v1/${encodeURIComponent(type)}/${encodeURIComponent(attribute)}`, responseType: "json" }));
|
|
1261
1384
|
}
|
|
1262
1385
|
|
|
@@ -1383,7 +1506,7 @@ async function localize(transport, tags, options = {}) {
|
|
|
1383
1506
|
const encoded = encodeURIComponent(toLson(unique));
|
|
1384
1507
|
const headers = options.culture ? { "Accept-Language": options.culture } : void 0;
|
|
1385
1508
|
const result = encoded.length <= (options.maxGetLength ?? DEFAULT_MAX_GET_LENGTH) ? await transport.request({ method: "GET", path: `/loc-2.v1?${encoded}`, headers, responseType: "json" }) : await transport.request({ method: "POST", path: "/loc-2.v1", headers, body: encoded, contentType: "application/x-www-form-urlencoded", responseType: "json", idempotent: true });
|
|
1386
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) throw new
|
|
1509
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) throw new ApiError(`loc-2.v1 answered an array of tags with ${JSON.stringify(result)?.slice(0, 100)}, not an object`, 200, "BAD_RESPONSE", result);
|
|
1387
1510
|
return result;
|
|
1388
1511
|
}
|
|
1389
1512
|
async function localizeOne(transport, tag, options = {}) {
|
|
@@ -1403,7 +1526,7 @@ function rejected(error) {
|
|
|
1403
1526
|
return messages.length ? new ApiError(messages.join("; "), error.status, "QUERY_REJECTED", error.details) : error;
|
|
1404
1527
|
}
|
|
1405
1528
|
async function queries2(transport, qs, options = {}) {
|
|
1406
|
-
for (const q of qs) if (!q.page) throw new
|
|
1529
|
+
for (const q of qs) if (!q.page) throw new UsageError(`query of ${q.from} without a page: every query.v1 read must be bounded`);
|
|
1407
1530
|
if (!qs.length) return [];
|
|
1408
1531
|
let response;
|
|
1409
1532
|
try {
|
|
@@ -1433,6 +1556,8 @@ __export(rest1_exports, {
|
|
|
1433
1556
|
history: () => history,
|
|
1434
1557
|
list: () => list2,
|
|
1435
1558
|
momentless: () => momentless,
|
|
1559
|
+
operate: () => operate,
|
|
1560
|
+
template: () => template,
|
|
1436
1561
|
toQuery: () => toQuery
|
|
1437
1562
|
});
|
|
1438
1563
|
var toRef = (doc) => ({ _oid: momentless(doc.idref) });
|
|
@@ -1464,8 +1589,8 @@ function fromAssetDocument(doc, { history: history2 = false } = {}) {
|
|
|
1464
1589
|
}
|
|
1465
1590
|
function toQuery(params) {
|
|
1466
1591
|
return {
|
|
1467
|
-
sel: params.sel?.join(","),
|
|
1468
|
-
where: params.where?.length ? params.where.join(";") : void 0,
|
|
1592
|
+
sel: (params.select ?? params.sel)?.join(","),
|
|
1593
|
+
where: (params.filter ?? params.where)?.length ? (params.filter ?? params.where).join(";") : void 0,
|
|
1469
1594
|
with: params.with ? Object.entries(params.with).map(([k, v]) => `${k}=${[].concat(v).join(",")}`).join("|") : void 0,
|
|
1470
1595
|
find: params.find,
|
|
1471
1596
|
findin: params.findin?.join(","),
|
|
@@ -1477,16 +1602,29 @@ function toQuery(params) {
|
|
|
1477
1602
|
};
|
|
1478
1603
|
}
|
|
1479
1604
|
async function read(transport, path, params, history2) {
|
|
1480
|
-
if (!params.page) throw new
|
|
1605
|
+
if (!params.page) throw new UsageError(`rest-1 read of ${path} without a page: every read must be bounded`);
|
|
1481
1606
|
const doc = await transport.request({ method: "GET", path, query: toQuery(params), responseType: "json" });
|
|
1482
1607
|
return { rows: (doc.Assets ?? []).map((a) => fromAssetDocument(a, { history: history2 })), total: typeof doc.total === "number" && doc.total >= 0 ? doc.total : null, pageSize: doc.pageSize, pageStart: doc.pageStart };
|
|
1483
1608
|
}
|
|
1484
1609
|
var list2 = (transport, type, params) => read(transport, `/rest-1.v1/Data/${encodeURIComponent(type)}`, params, false);
|
|
1485
|
-
async function get2(transport, oid,
|
|
1610
|
+
async function get2(transport, oid, select) {
|
|
1486
1611
|
const [type, id] = momentless(oid).split(":");
|
|
1487
|
-
const doc = await transport.request({ method: "GET", path: `/rest-1.v1/Data/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, query: { sel:
|
|
1612
|
+
const doc = await transport.request({ method: "GET", path: `/rest-1.v1/Data/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, query: { sel: select?.join(",") }, responseType: "json" });
|
|
1488
1613
|
return fromAssetDocument(doc);
|
|
1489
1614
|
}
|
|
1615
|
+
async function operate(transport, oid, op) {
|
|
1616
|
+
const [type, id] = momentless(oid).split(":");
|
|
1617
|
+
if (!type || !id) throw new UsageError(`"${oid}" is not an OID token (Story:1234)`);
|
|
1618
|
+
const doc = await transport.request({ method: "POST", path: `/rest-1.v1/Data/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, query: { op }, responseType: "json" });
|
|
1619
|
+
if (!doc || typeof doc.id !== "string" || !doc.id) throw new ApiError(`rest-1 ${op} on ${oid} did not answer with an asset`, 200, "BAD_RESPONSE", doc);
|
|
1620
|
+
const moment = doc.id.split(":")[2] ?? null;
|
|
1621
|
+
return { oid: momentless(doc.id), moment };
|
|
1622
|
+
}
|
|
1623
|
+
async function template(transport, type, options = {}) {
|
|
1624
|
+
const doc = await transport.request({ method: "GET", path: `/rest-1.v1/New/${encodeURIComponent(type)}`, query: { ctx: options.ctx ? momentless(options.ctx) : void 0 }, responseType: "json" });
|
|
1625
|
+
const { _oid: _ignored, ...values } = fromAssetDocument({ ...doc, id: doc.id || `${type}:0` });
|
|
1626
|
+
return values;
|
|
1627
|
+
}
|
|
1490
1628
|
var history = (transport, target, params) => {
|
|
1491
1629
|
const [type, id] = target.split(":");
|
|
1492
1630
|
const path = id ? `/rest-1.v1/Hist/${encodeURIComponent(type)}/${encodeURIComponent(id)}` : `/rest-1.v1/Hist/${encodeURIComponent(type)}`;
|
|
@@ -1494,6 +1632,8 @@ var history = (transport, target, params) => {
|
|
|
1494
1632
|
};
|
|
1495
1633
|
|
|
1496
1634
|
// src/client.ts
|
|
1635
|
+
var DEFAULT_LIMIT = 1e3;
|
|
1636
|
+
var ME_SELECT = ["Name", "Username", "Nickname", "Email"];
|
|
1497
1637
|
var AgilityApiClient = class _AgilityApiClient {
|
|
1498
1638
|
transport;
|
|
1499
1639
|
schema;
|
|
@@ -1525,6 +1665,48 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1525
1665
|
const transport = { request: (options) => base.request({ ...options, signal: options.signal ?? signal }) };
|
|
1526
1666
|
return new _AgilityApiClient(this.config, { transport, schema: this.schema });
|
|
1527
1667
|
}
|
|
1668
|
+
async query(q) {
|
|
1669
|
+
await this.schema.ensureFor([q]);
|
|
1670
|
+
return query2(this.transport, q, { lookup: this.schema });
|
|
1671
|
+
}
|
|
1672
|
+
async queries(qs) {
|
|
1673
|
+
await this.schema.ensureFor(qs);
|
|
1674
|
+
return queries2(this.transport, [...qs], { lookup: this.schema });
|
|
1675
|
+
}
|
|
1676
|
+
async get(oid, select) {
|
|
1677
|
+
const rows = await this.query({ from: momentless(oid), ...select ? { select } : {}, page: { size: 1, start: 0 } });
|
|
1678
|
+
return rows[0] ?? null;
|
|
1679
|
+
}
|
|
1680
|
+
async queryPage(q) {
|
|
1681
|
+
await this.schema.ensureFor([q]);
|
|
1682
|
+
return queryPage(this.transport, q, { lookup: this.schema });
|
|
1683
|
+
}
|
|
1684
|
+
async *pages(q, options = {}) {
|
|
1685
|
+
if (!q.page) throw new UsageError(`query of ${q.from} without a page: every read must be bounded`);
|
|
1686
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
1687
|
+
const size = q.page.size;
|
|
1688
|
+
let start = q.page.start, read2 = 0;
|
|
1689
|
+
while (read2 < limit) {
|
|
1690
|
+
const want = Math.min(size, limit - read2);
|
|
1691
|
+
const rows = await this.query({ ...q, page: { size: want, start } });
|
|
1692
|
+
if (rows.length) yield rows;
|
|
1693
|
+
read2 += rows.length;
|
|
1694
|
+
start += rows.length;
|
|
1695
|
+
if (rows.length < want) return;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
async all(q, options = {}) {
|
|
1699
|
+
const out = [];
|
|
1700
|
+
for await (const page2 of this.pages(q, options)) out.push(...page2);
|
|
1701
|
+
return out;
|
|
1702
|
+
}
|
|
1703
|
+
/** The member these credentials belong to (Member.IsSelf, live 2026-11-12): what a comment's Author is. */
|
|
1704
|
+
async me() {
|
|
1705
|
+
const [row] = await this.query({ from: "Member", select: ME_SELECT, filter: ["IsSelf='true'"], page: { size: 1, start: 0 } });
|
|
1706
|
+
if (!row) throw new ApiError("the server knows no member for these credentials", 0, "NOT_FOUND");
|
|
1707
|
+
return row;
|
|
1708
|
+
}
|
|
1709
|
+
// ---- the endpoints behind the reads -----------------------------------------------
|
|
1528
1710
|
/** meta.v1: the instance's schema, raw. (The translated, cached view is `schema`.) */
|
|
1529
1711
|
meta = {
|
|
1530
1712
|
metamodel: () => getMetamodel(this.transport),
|
|
@@ -1533,12 +1715,88 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1533
1715
|
};
|
|
1534
1716
|
/**
|
|
1535
1717
|
* loc-2.v1: labels for tags, in one request (meta's DisplayName values are
|
|
1536
|
-
* tags). An unmatched tag comes back as
|
|
1537
|
-
* "missing", so neither does this.
|
|
1718
|
+
* tags, and so is a violation's `tag`). An unmatched tag comes back as
|
|
1719
|
+
* itself; the server never says "missing", so neither does this.
|
|
1538
1720
|
*/
|
|
1539
1721
|
localize(tags, options) {
|
|
1540
1722
|
return localize(this.transport, tags, options);
|
|
1541
1723
|
}
|
|
1724
|
+
/**
|
|
1725
|
+
* rest-1.v1, for what the other endpoints cannot do: deleted assets
|
|
1726
|
+
* (`deleted: true`), one asset by OID without a page, history (one row
|
|
1727
|
+
* per moment), a new asset's template (`template`: what the type's
|
|
1728
|
+
* initializer fills in for a context — a Timebox's next name and dates,
|
|
1729
|
+
* a Story's carry-over from a Request), and an operation's result
|
|
1730
|
+
* (`operate`: api/asset performs a Copy but never says what it made;
|
|
1731
|
+
* rest-1 answers the new OID). Parameters are spelled as a query's
|
|
1732
|
+
* (`select`, `filter`).
|
|
1733
|
+
*/
|
|
1734
|
+
data = {
|
|
1735
|
+
list: (type, params) => list2(this.transport, type, params),
|
|
1736
|
+
get: (oid, select) => get2(this.transport, oid, select),
|
|
1737
|
+
history: (target, params) => history(this.transport, target, params),
|
|
1738
|
+
template: (type, options) => template(this.transport, type, options),
|
|
1739
|
+
operate: (oid, op) => operate(this.transport, oid, op)
|
|
1740
|
+
};
|
|
1741
|
+
/** Copy an asset (the server's Copy operation) and answer the copy's OID: the one thing api/asset's `execute: "Copy"` does not say. */
|
|
1742
|
+
async copy(oid) {
|
|
1743
|
+
return (await operate(this.transport, oid, "Copy")).oid;
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* What a form needs for an asset type in a scope: the attributes the
|
|
1747
|
+
* server requires to create one (meta), the ones the product shows there
|
|
1748
|
+
* (api/visibleFields, custom fields included once configured), and the
|
|
1749
|
+
* scope's scheme's important fields and whether they are enforced at
|
|
1750
|
+
* close. `visible` alone answers for several types and scopes at once.
|
|
1751
|
+
*/
|
|
1752
|
+
fields = {
|
|
1753
|
+
describe: (type, scope) => describe2(this.transport, this.schema, type, scope),
|
|
1754
|
+
visible: (types, scopes) => visibleFields(this.transport, types, scopes),
|
|
1755
|
+
visibleByAsset: (oid) => visibleFieldsByAsset(this.transport, oid)
|
|
1756
|
+
};
|
|
1757
|
+
/** @deprecated since 1.0.0-alpha.3: use `client.query` / `client.queries` (the same endpoint, query.v1; only the name moved). Removed in 1.0.0. */
|
|
1758
|
+
queryV1 = {
|
|
1759
|
+
query: (q) => this.query(q),
|
|
1760
|
+
queries: (qs) => this.queries(qs)
|
|
1761
|
+
};
|
|
1762
|
+
// ---- writes -----------------------------------------------------------------------
|
|
1763
|
+
/** api/asset: batches, answered in the model's terms. */
|
|
1764
|
+
assets = {
|
|
1765
|
+
/**
|
|
1766
|
+
* Send a batch. The Outcome says what it did — created, modified and
|
|
1767
|
+
* operated OIDs, failures by index (each with its parsed violation),
|
|
1768
|
+
* query rows per query command — and never the envelope. api/asset
|
|
1769
|
+
* commits per command and answers 200 either way, so a failed command
|
|
1770
|
+
* is in `failures`, or thrown as a CommandFailureError with
|
|
1771
|
+
* `throwOnFailure` (the error carries the same Outcome).
|
|
1772
|
+
*/
|
|
1773
|
+
send: async (commands, options) => {
|
|
1774
|
+
await this.schema.ensureFor(commands);
|
|
1775
|
+
return outcomeOf(await send(this.transport, commands, { ...options, lookup: this.schema }));
|
|
1776
|
+
},
|
|
1777
|
+
/** The server's projection of a batch: one command per asset it would touch, nothing applied. */
|
|
1778
|
+
preview: (commands) => preview(this.transport, commands),
|
|
1779
|
+
/**
|
|
1780
|
+
* Send as a compare-and-set: each asset is changed only if nobody has
|
|
1781
|
+
* changed it since the preview read it; the rest come back `skipped`.
|
|
1782
|
+
* Throws UnguardableError, before any request, for shapes it cannot
|
|
1783
|
+
* cover.
|
|
1784
|
+
*/
|
|
1785
|
+
sendGuarded: async (commands, options) => {
|
|
1786
|
+
await this.schema.ensureFor(commands);
|
|
1787
|
+
return sendGuarded(this.transport, commands, { ...options, lookup: this.schema });
|
|
1788
|
+
},
|
|
1789
|
+
/** @deprecated since 1.0.0-alpha.3: use `client.query` (rows, through query.v1) or `client.queryPage` (rows and a total, through api/asset as this does). Removed in 1.0.0. */
|
|
1790
|
+
query: async (q) => {
|
|
1791
|
+
await this.schema.ensureFor([q]);
|
|
1792
|
+
return query(this.transport, q, { lookup: this.schema });
|
|
1793
|
+
},
|
|
1794
|
+
/** @deprecated since 1.0.0-alpha.3: use `client.queries`. Removed in 1.0.0. */
|
|
1795
|
+
queries: async (qs) => {
|
|
1796
|
+
await this.schema.ensureFor(qs);
|
|
1797
|
+
return queries(this.transport, qs, { lookup: this.schema });
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1542
1800
|
/**
|
|
1543
1801
|
* Services: what a person types, turned into assets. Numbers resolve in
|
|
1544
1802
|
* one api/asset request across every number root; an OID token is taken
|
|
@@ -1561,7 +1819,7 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1561
1819
|
/**
|
|
1562
1820
|
* The files attached to assets: metadata on Attachment assets, bytes
|
|
1563
1821
|
* through attachment.img. `add` sends a small file inline (one request)
|
|
1564
|
-
* and a large one in two steps
|
|
1822
|
+
* and a large one in two steps.
|
|
1565
1823
|
*/
|
|
1566
1824
|
attachments = {
|
|
1567
1825
|
list: (asset, options) => list(this.transport, asset, options),
|
|
@@ -1571,7 +1829,8 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1571
1829
|
add: (asset, data, options) => add(this.transport, asset, data, options),
|
|
1572
1830
|
put: (id, data, contentType) => put(this.transport, id, data, contentType),
|
|
1573
1831
|
remove: (id) => remove(this.transport, id),
|
|
1574
|
-
|
|
1832
|
+
/** @deprecated since 1.0.0-alpha.3: `client.urls.attachment`. Removed in 1.0.0. */
|
|
1833
|
+
url: (id) => this.urls.attachment(id)
|
|
1575
1834
|
};
|
|
1576
1835
|
/**
|
|
1577
1836
|
* Images embedded in an asset's rich text (Description and other long
|
|
@@ -1581,55 +1840,27 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1581
1840
|
*/
|
|
1582
1841
|
embeddedImages = {
|
|
1583
1842
|
upload: (asset, data, options) => uploadEmbeddedImage(this.transport, asset, data, options),
|
|
1584
|
-
|
|
1843
|
+
/** @deprecated since 1.0.0-alpha.3: `client.urls.embeddedImage`. Removed in 1.0.0. */
|
|
1844
|
+
url: (id) => this.urls.embeddedImage(id),
|
|
1585
1845
|
normalize: (html) => normalizeEmbeddedImageUrls(html, this.config.baseUrl)
|
|
1586
1846
|
};
|
|
1587
|
-
/**
|
|
1588
|
-
|
|
1589
|
-
list: (type, params) => list2(this.transport, type, params),
|
|
1590
|
-
get: (oid, sel) => get2(this.transport, oid, sel),
|
|
1591
|
-
history: (target, params) => history(this.transport, target, params)
|
|
1592
|
-
};
|
|
1593
|
-
/**
|
|
1594
|
-
* query.v1: reads only, the same query bodies as assets.query, rows
|
|
1595
|
-
* translated the same way. The endpoint cannot write, so it is the safe
|
|
1596
|
-
* place to explore a query; its requests may be retried like a GET.
|
|
1597
|
-
*/
|
|
1598
|
-
queryV1 = {
|
|
1599
|
-
query: async (q) => {
|
|
1600
|
-
await this.schema.ensureFor([q]);
|
|
1601
|
-
return query2(this.transport, q, { lookup: this.schema });
|
|
1602
|
-
},
|
|
1603
|
-
queries: async (qs) => {
|
|
1604
|
-
await this.schema.ensureFor(qs);
|
|
1605
|
-
return queries2(this.transport, qs, { lookup: this.schema });
|
|
1606
|
-
}
|
|
1607
|
-
};
|
|
1608
|
-
/** api/asset: batches and queries, rows translated through the schema. */
|
|
1609
|
-
assets = {
|
|
1610
|
-
send: async (commands, options) => {
|
|
1611
|
-
await this.schema.ensureFor(commands);
|
|
1612
|
-
return send(this.transport, commands, { ...options, lookup: this.schema });
|
|
1613
|
-
},
|
|
1614
|
-
preview: (commands) => preview(this.transport, commands),
|
|
1847
|
+
/** Where a browser opens things on this instance: an asset's page, an attachment's bytes, an embedded image. */
|
|
1848
|
+
urls = {
|
|
1615
1849
|
/**
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1618
|
-
*
|
|
1619
|
-
*
|
|
1850
|
+
* The asset's page, from an OID token or an asset Number (S-01001,
|
|
1851
|
+
* what a person typed and what a message shows): assetdetail.v1
|
|
1852
|
+
* resolves either and redirects to the type's own detail page; a signed-
|
|
1853
|
+
* out reader lands there after logging in. Anything else is a UsageError.
|
|
1620
1854
|
*/
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
return query(this.transport, q, { lookup: this.schema });
|
|
1855
|
+
asset: (ref) => {
|
|
1856
|
+
const base = this.config.baseUrl.replace(/\/+$/, "");
|
|
1857
|
+
const text = ref.trim();
|
|
1858
|
+
if (isOidToken(text)) return `${base}/assetdetail.v1?oid=${momentless(text)}`;
|
|
1859
|
+
if (isAssetNumber(text)) return `${base}/assetdetail.v1?Number=${encodeURIComponent(text)}`;
|
|
1860
|
+
throw new UsageError(`"${ref}" is neither an OID token (Story:1234) nor an asset number (S-1234)`);
|
|
1628
1861
|
},
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
return queries(this.transport, qs, { lookup: this.schema });
|
|
1632
|
-
}
|
|
1862
|
+
attachment: (id) => attachmentUrl(this.config.baseUrl, id),
|
|
1863
|
+
embeddedImage: (id) => embeddedImageUrl(this.config.baseUrl, id)
|
|
1633
1864
|
};
|
|
1634
1865
|
};
|
|
1635
1866
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1649,19 +1880,25 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1649
1880
|
TypeCache,
|
|
1650
1881
|
UNGUARDABLE_TYPES,
|
|
1651
1882
|
UnguardableError,
|
|
1883
|
+
UsageError,
|
|
1884
|
+
WrappedError,
|
|
1652
1885
|
assetApi,
|
|
1653
1886
|
assetStateOf,
|
|
1654
1887
|
attachment,
|
|
1655
1888
|
createFetchTransport,
|
|
1656
1889
|
describeBatch,
|
|
1890
|
+
fields,
|
|
1657
1891
|
guard,
|
|
1892
|
+
importantName,
|
|
1658
1893
|
isAssetNumber,
|
|
1659
1894
|
isOidToken,
|
|
1895
|
+
kindOf,
|
|
1660
1896
|
loc2,
|
|
1661
1897
|
meta,
|
|
1662
1898
|
momentless,
|
|
1663
1899
|
namesOf,
|
|
1664
1900
|
parseOid,
|
|
1901
|
+
parseViolation,
|
|
1665
1902
|
queryV1,
|
|
1666
1903
|
resolveNumbers,
|
|
1667
1904
|
resolveRef,
|