@versionone/agility-sdk 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +171 -0
- package/README.md +9 -3
- package/dist/index.cjs +225 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +301 -107
- package/dist/index.d.ts +301 -107
- package/dist/index.js +223 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -117,10 +117,22 @@ function messageOf(data, status) {
|
|
|
117
117
|
return `HTTP ${status}`;
|
|
118
118
|
}
|
|
119
119
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
120
|
+
var MAX_RETRY_AFTER_MS = 3e4;
|
|
121
|
+
function retryDelay(response, attempt) {
|
|
122
|
+
const header = response.headers.get("retry-after");
|
|
123
|
+
if (header) {
|
|
124
|
+
const seconds = Number(header);
|
|
125
|
+
const ms = Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(header) - Date.now();
|
|
126
|
+
if (Number.isFinite(ms) && ms > MAX_RETRY_AFTER_MS) return null;
|
|
127
|
+
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
128
|
+
}
|
|
129
|
+
return 150 * (attempt + 1);
|
|
130
|
+
}
|
|
131
|
+
var retriable = (status) => status === 429 || status >= 500;
|
|
120
132
|
function createFetchTransport(config) {
|
|
121
133
|
const fetchImpl = config.fetchImpl ?? fetch;
|
|
122
134
|
const timeoutMs = config.timeoutMs ?? 6e4;
|
|
123
|
-
const retries = Math.max(0, config.retries ??
|
|
135
|
+
const retries = Math.max(0, config.retries ?? 1);
|
|
124
136
|
const authorization = config.authorization ?? (config.accessToken ? `Bearer ${config.accessToken}` : void 0);
|
|
125
137
|
const unauthorizedStatuses = config.unauthorizedStatuses ?? [401];
|
|
126
138
|
return {
|
|
@@ -163,6 +175,10 @@ function createFetchTransport(config) {
|
|
|
163
175
|
options.onResponse?.({ status: response.status, headers: response.headers });
|
|
164
176
|
return await readBody(response, responseType);
|
|
165
177
|
}
|
|
178
|
+
if (response.status === 304 && options.acceptNotModified) {
|
|
179
|
+
options.onResponse?.({ status: 304, headers: response.headers });
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
166
182
|
const data = await readBody(response, responseType === "bytes" ? "text" : responseType === "json" ? "auto" : responseType);
|
|
167
183
|
lastError = new ApiError(messageOf(data, response.status), response.status, void 0, data);
|
|
168
184
|
} catch (error) {
|
|
@@ -181,8 +197,10 @@ function createFetchTransport(config) {
|
|
|
181
197
|
}
|
|
182
198
|
break;
|
|
183
199
|
}
|
|
184
|
-
if (response.status
|
|
185
|
-
|
|
200
|
+
if (retriable(response.status) && attempt + 1 < attempts) {
|
|
201
|
+
const wait = retryDelay(response, attempt);
|
|
202
|
+
if (wait === null) break;
|
|
203
|
+
await delay(wait);
|
|
186
204
|
continue;
|
|
187
205
|
}
|
|
188
206
|
break;
|
|
@@ -389,24 +407,29 @@ function toNumber(value) {
|
|
|
389
407
|
const n = typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
390
408
|
return Number.isNaN(n) ? value : n;
|
|
391
409
|
}
|
|
410
|
+
function failureDetail(index, message, echoed) {
|
|
411
|
+
const detail = { index, message, violation: parseViolation(message) };
|
|
412
|
+
Object.defineProperty(detail, "echoed", { value: echoed, enumerable: false, writable: false });
|
|
413
|
+
return detail;
|
|
414
|
+
}
|
|
392
415
|
function parseViolation(message) {
|
|
393
416
|
const parts = message.trim().split("'");
|
|
394
417
|
if (parts[0] !== "Violation" || !parts[1]) return null;
|
|
395
418
|
const rule = parts[1], rest = parts.slice(2).filter((p) => p !== "");
|
|
396
419
|
if (rest[0] === "AttributeDefinition" && rest[1] && rest[2]) return { rule, attribute: `${rest[2]}.${rest[1]}`, tag: rest.join("'") };
|
|
397
420
|
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;
|
|
398
|
-
return { rule, attribute: token, tag: rest.length ? rest.join("'") : null };
|
|
421
|
+
return { rule, attribute: token, tag: !token && rest.length > 1 ? rest.join("'") : null };
|
|
399
422
|
}
|
|
400
423
|
function failuresOf(response) {
|
|
401
|
-
return (response.commandFailures?.commands ?? []).map(({ error, ...echoed }) => (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
echoed
|
|
405
|
-
|
|
406
|
-
}));
|
|
424
|
+
return (response.commandFailures?.commands ?? []).map(({ error, ...echoed }) => failureDetail(
|
|
425
|
+
typeof error === "object" && error && typeof error.sourceCommandIndex === "number" ? error.sourceCommandIndex : null,
|
|
426
|
+
typeof error === "string" ? error : error?.message || "command failed",
|
|
427
|
+
echoed
|
|
428
|
+
));
|
|
407
429
|
}
|
|
408
430
|
function outcomeOf(response) {
|
|
409
431
|
return {
|
|
432
|
+
requestId: typeof response.requestId === "string" && response.requestId ? response.requestId : null,
|
|
410
433
|
created: (response.assetsCreated?.oidTokens ?? []).map(momentless),
|
|
411
434
|
modified: (response.assetsModified?.oidTokens ?? []).map(momentless),
|
|
412
435
|
operated: (response.assetsOperatedOn?.oidTokens ?? []).map(momentless),
|
|
@@ -415,13 +438,14 @@ function outcomeOf(response) {
|
|
|
415
438
|
durationSeconds: typeof response.durationSeconds === "number" ? response.durationSeconds : null
|
|
416
439
|
};
|
|
417
440
|
}
|
|
418
|
-
var emptyOutcome = () => ({ created: [], modified: [], operated: [], failures: [], queryResults: [], durationSeconds: null });
|
|
441
|
+
var emptyOutcome = () => ({ requestId: null, created: [], modified: [], operated: [], failures: [], queryResults: [], durationSeconds: null });
|
|
419
442
|
function mergeOutcome(into, done, offset) {
|
|
420
443
|
into.created.push(...done.created);
|
|
421
444
|
into.modified.push(...done.modified);
|
|
422
445
|
into.operated.push(...done.operated);
|
|
423
|
-
into.failures.push(...done.failures.map((f) => (
|
|
446
|
+
into.failures.push(...done.failures.map((f) => failureDetail(f.index === null ? null : f.index + offset, f.message, f.echoed)));
|
|
424
447
|
into.queryResults.push(...done.queryResults);
|
|
448
|
+
into.requestId = null;
|
|
425
449
|
if (done.durationSeconds !== null) into.durationSeconds = (into.durationSeconds ?? 0) + done.durationSeconds;
|
|
426
450
|
return into;
|
|
427
451
|
}
|
|
@@ -1102,6 +1126,65 @@ async function reversible(transport, receipt, options = {}) {
|
|
|
1102
1126
|
return { commands, drifted, reactivated };
|
|
1103
1127
|
}
|
|
1104
1128
|
|
|
1129
|
+
// src/wire/fields.ts
|
|
1130
|
+
var fields_exports = {};
|
|
1131
|
+
__export(fields_exports, {
|
|
1132
|
+
visibleFields: () => visibleFields,
|
|
1133
|
+
visibleFieldsByAsset: () => visibleFieldsByAsset
|
|
1134
|
+
});
|
|
1135
|
+
async function visibleFields(transport, types, scopes) {
|
|
1136
|
+
if (!types.length) return {};
|
|
1137
|
+
const params = [...scopes.map((s) => `scopeOid=${encodeURIComponent(momentless(s))}`), ...types.map((t) => `assetTypeToken=${encodeURIComponent(t)}`)].join("&");
|
|
1138
|
+
const answer = await transport.request({ method: "GET", path: `/api/visibleFields?${params}`, responseType: "json" });
|
|
1139
|
+
if (!Array.isArray(answer)) throw new ApiError("api/visibleFields did not answer a list", 200, "BAD_RESPONSE", answer);
|
|
1140
|
+
const out = {};
|
|
1141
|
+
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") : [];
|
|
1142
|
+
return out;
|
|
1143
|
+
}
|
|
1144
|
+
async function visibleFieldsByAsset(transport, oid) {
|
|
1145
|
+
const answer = await transport.request({ method: "GET", path: "/api/visibleFieldsByAsset", query: { oidToken: momentless(oid) }, responseType: "json" });
|
|
1146
|
+
if (!Array.isArray(answer)) throw new ApiError("api/visibleFieldsByAsset did not answer a list", 200, "BAD_RESPONSE", answer);
|
|
1147
|
+
return answer.filter((f) => typeof f === "string");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/services/fields.ts
|
|
1151
|
+
async function lineage(schema, type) {
|
|
1152
|
+
const out = [];
|
|
1153
|
+
for (let t = type; t && !out.includes(t); ) {
|
|
1154
|
+
out.push(t);
|
|
1155
|
+
const def = await schema.load(t);
|
|
1156
|
+
t = def?.Base;
|
|
1157
|
+
}
|
|
1158
|
+
return out;
|
|
1159
|
+
}
|
|
1160
|
+
function importantName(token, types) {
|
|
1161
|
+
const dot = token.indexOf(".");
|
|
1162
|
+
if (dot < 0) return token;
|
|
1163
|
+
return types.includes(token.slice(0, dot)) ? token.slice(dot + 1) : null;
|
|
1164
|
+
}
|
|
1165
|
+
async function describe2(transport, schema, type, scope) {
|
|
1166
|
+
const scopeOid = momentless(scope);
|
|
1167
|
+
const types = await lineage(schema, type);
|
|
1168
|
+
const definition = await schema.load(type);
|
|
1169
|
+
const required = definition ? Object.values(definition.Attributes).filter((a) => a.IsRequired && !a.IsReadOnly).map((a) => a.Name).sort() : [];
|
|
1170
|
+
const [visible, [row]] = await Promise.all([
|
|
1171
|
+
visibleFields(transport, [type], [scopeOid]),
|
|
1172
|
+
query(transport, { from: "Scope", select: [{ from: "Scheme", select: ["ImportantFields", "IsRequiredAtClose"] }], filter: ["ID=$scope"], with: { $scope: scopeOid }, page: { size: 1, start: 0 } }, { lookup: schema })
|
|
1173
|
+
]);
|
|
1174
|
+
const scheme = row?.Scheme ?? null;
|
|
1175
|
+
const tokens = Array.isArray(scheme?.ImportantFields) ? scheme.ImportantFields.filter((t) => typeof t === "string") : [];
|
|
1176
|
+
const important = [...new Set(tokens.map((t) => importantName(t, types)).filter((n) => !!n))].sort();
|
|
1177
|
+
return {
|
|
1178
|
+
type,
|
|
1179
|
+
scope: scopeOid,
|
|
1180
|
+
scheme: scheme ? momentless(scheme._oid) : null,
|
|
1181
|
+
required,
|
|
1182
|
+
visible: visible[type] ?? [],
|
|
1183
|
+
important,
|
|
1184
|
+
requiredAtClose: scheme?.IsRequiredAtClose === true
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1105
1188
|
// src/types/asset.ts
|
|
1106
1189
|
var ASSET_STATE_CODES = { Future: 0, Active: 64, Closed: 128, Dead: 192, Template: 200, OldBrokenDownStory: 208, Deprecated: 254, Deleted: 255 };
|
|
1107
1190
|
var STATE_OF_CODE = Object.fromEntries(Object.entries(ASSET_STATE_CODES).map(([name, code]) => [code, name]));
|
|
@@ -1194,10 +1277,13 @@ async function resolveRef(transport, spec, options = {}) {
|
|
|
1194
1277
|
// src/wire/meta.ts
|
|
1195
1278
|
var meta_exports = {};
|
|
1196
1279
|
__export(meta_exports, {
|
|
1280
|
+
etag: () => etag,
|
|
1197
1281
|
getAssetType: () => getAssetType,
|
|
1198
1282
|
getAttributeDefinition: () => getAttributeDefinition,
|
|
1199
1283
|
getMetamodel: () => getMetamodel,
|
|
1200
1284
|
isMetamodelDocument: () => isMetamodelDocument,
|
|
1285
|
+
readAssetType: () => readAssetType,
|
|
1286
|
+
readMetamodel: () => readMetamodel,
|
|
1201
1287
|
toAssetType: () => toAssetType,
|
|
1202
1288
|
toAttributeDefinition: () => toAttributeDefinition,
|
|
1203
1289
|
toMetamodel: () => toMetamodel
|
|
@@ -1242,12 +1328,36 @@ function toMetamodel(doc) {
|
|
|
1242
1328
|
function isMetamodelDocument(value) {
|
|
1243
1329
|
return !!value && typeof value === "object" && typeof value.Version === "string" && !!value.AssetTypes && typeof value.AssetTypes === "object";
|
|
1244
1330
|
}
|
|
1331
|
+
var PROBE_PATH = "/meta.v1/BaseAsset/Name";
|
|
1332
|
+
async function conditional(transport, path, options) {
|
|
1333
|
+
let etag2 = null, status = 200;
|
|
1334
|
+
const onResponse = (r) => {
|
|
1335
|
+
status = r.status;
|
|
1336
|
+
etag2 = r.headers.get("etag");
|
|
1337
|
+
};
|
|
1338
|
+
const headers = options.ifNoneMatch ? { "if-none-match": options.ifNoneMatch } : void 0;
|
|
1339
|
+
const doc = await transport.request({ method: "GET", path, headers, responseType: "json", onResponse, acceptNotModified: !!options.ifNoneMatch });
|
|
1340
|
+
if (status === 304 || doc === void 0) return null;
|
|
1341
|
+
return { value: doc, etag: etag2 };
|
|
1342
|
+
}
|
|
1245
1343
|
async function getMetamodel(transport) {
|
|
1246
1344
|
return toMetamodel(await transport.request({ method: "GET", path: "/meta.v1", responseType: "json" }));
|
|
1247
1345
|
}
|
|
1346
|
+
async function readMetamodel(transport, options = {}) {
|
|
1347
|
+
const read2 = await conditional(transport, "/meta.v1", options);
|
|
1348
|
+
return read2 && { value: toMetamodel(read2.value), etag: read2.etag };
|
|
1349
|
+
}
|
|
1248
1350
|
async function getAssetType(transport, token) {
|
|
1249
1351
|
return toAssetType(await transport.request({ method: "GET", path: `/meta.v1/${encodeURIComponent(token)}`, responseType: "json" }));
|
|
1250
1352
|
}
|
|
1353
|
+
async function readAssetType(transport, token, options = {}) {
|
|
1354
|
+
const read2 = await conditional(transport, `/meta.v1/${encodeURIComponent(token)}`, options);
|
|
1355
|
+
return read2 && { value: toAssetType(read2.value), etag: read2.etag };
|
|
1356
|
+
}
|
|
1357
|
+
async function etag(transport, options = {}) {
|
|
1358
|
+
const read2 = await conditional(transport, PROBE_PATH, options);
|
|
1359
|
+
return read2 ? read2.etag : options.ifNoneMatch ?? null;
|
|
1360
|
+
}
|
|
1251
1361
|
async function getAttributeDefinition(transport, tokenref) {
|
|
1252
1362
|
const [type, attribute] = tokenref.split(".");
|
|
1253
1363
|
if (!type || !attribute) throw new UsageError(`"${tokenref}" is not an attribute token ("Story.Name")`);
|
|
@@ -1259,6 +1369,13 @@ var TypeCache = class _TypeCache {
|
|
|
1259
1369
|
/** Loaded types; null records that the instance has no such type, so it is not asked for again. */
|
|
1260
1370
|
types = /* @__PURE__ */ new Map();
|
|
1261
1371
|
loading = /* @__PURE__ */ new Map();
|
|
1372
|
+
/** The schema's tag the loaded types came with (meta.v1's ETag, one for the whole schema); null until a load, or for a seeded cache. */
|
|
1373
|
+
etag = null;
|
|
1374
|
+
/** Forget every loaded type (and the tag): the next query loads afresh. In-flight loads finish into the cache as before. */
|
|
1375
|
+
clear() {
|
|
1376
|
+
this.types.clear();
|
|
1377
|
+
this.etag = null;
|
|
1378
|
+
}
|
|
1262
1379
|
/** A cache seeded from a metamodel document. */
|
|
1263
1380
|
static fromMetamodel(metamodel) {
|
|
1264
1381
|
const cache = new _TypeCache();
|
|
@@ -1323,7 +1440,10 @@ var Schema = class _Schema {
|
|
|
1323
1440
|
this.types.set(token, null);
|
|
1324
1441
|
return null;
|
|
1325
1442
|
}
|
|
1326
|
-
const request =
|
|
1443
|
+
const request = readAssetType(this.transport, token).then((read2) => {
|
|
1444
|
+
if (read2?.etag) this.cache.etag = read2.etag;
|
|
1445
|
+
return read2 ? read2.value : null;
|
|
1446
|
+
}).catch((error) => {
|
|
1327
1447
|
if (error instanceof ApiError && error.status === 404) return null;
|
|
1328
1448
|
throw error;
|
|
1329
1449
|
}).then((type) => {
|
|
@@ -1333,6 +1453,23 @@ var Schema = class _Schema {
|
|
|
1333
1453
|
this.loading.set(token, request);
|
|
1334
1454
|
return request;
|
|
1335
1455
|
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Has the instance's schema changed since these types were loaded? One
|
|
1458
|
+
* small conditional read of meta.v1. Unchanged: nothing happens.
|
|
1459
|
+
* Changed (or never tagged, or the transport cannot see headers): the
|
|
1460
|
+
* loaded types are dropped, so the next query loads them again, and the
|
|
1461
|
+
* new tag is kept. Answers whether it changed and the current tag. A
|
|
1462
|
+
* schema without a transport (seeded) never changes.
|
|
1463
|
+
*/
|
|
1464
|
+
async revalidate() {
|
|
1465
|
+
if (!this.transport) return { changed: false, etag: this.cache.etag };
|
|
1466
|
+
const was = this.cache.etag;
|
|
1467
|
+
const now2 = await etag(this.transport, { ifNoneMatch: was });
|
|
1468
|
+
if (was && now2 === was) return { changed: false, etag: was };
|
|
1469
|
+
this.cache.clear();
|
|
1470
|
+
this.cache.etag = now2;
|
|
1471
|
+
return { changed: true, etag: now2 };
|
|
1472
|
+
}
|
|
1336
1473
|
/**
|
|
1337
1474
|
* Load whatever the queries in a batch need to have their rows translated:
|
|
1338
1475
|
* the `from` types, then the types behind every relation hop and sub-select,
|
|
@@ -1393,8 +1530,8 @@ __export(queryV1_exports, {
|
|
|
1393
1530
|
});
|
|
1394
1531
|
function rejected(error) {
|
|
1395
1532
|
const details = error.details;
|
|
1396
|
-
const
|
|
1397
|
-
return
|
|
1533
|
+
const rejections = (details?.exceptions ?? []).filter((e) => e && typeof e.message === "string" && e.message).map((e) => ({ type: typeof e.type === "string" ? e.type : null, message: e.message }));
|
|
1534
|
+
return rejections.length ? new ApiError(rejections.map((r) => r.message).join("; "), error.status, "QUERY_REJECTED", rejections) : error;
|
|
1398
1535
|
}
|
|
1399
1536
|
async function queries2(transport, qs, options = {}) {
|
|
1400
1537
|
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`);
|
|
@@ -1427,6 +1564,8 @@ __export(rest1_exports, {
|
|
|
1427
1564
|
history: () => history,
|
|
1428
1565
|
list: () => list2,
|
|
1429
1566
|
momentless: () => momentless,
|
|
1567
|
+
operate: () => operate,
|
|
1568
|
+
template: () => template,
|
|
1430
1569
|
toQuery: () => toQuery
|
|
1431
1570
|
});
|
|
1432
1571
|
var toRef = (doc) => ({ _oid: momentless(doc.idref) });
|
|
@@ -1481,6 +1620,19 @@ async function get2(transport, oid, select) {
|
|
|
1481
1620
|
const doc = await transport.request({ method: "GET", path: `/rest-1.v1/Data/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, query: { sel: select?.join(",") }, responseType: "json" });
|
|
1482
1621
|
return fromAssetDocument(doc);
|
|
1483
1622
|
}
|
|
1623
|
+
async function operate(transport, oid, op) {
|
|
1624
|
+
const [type, id] = momentless(oid).split(":");
|
|
1625
|
+
if (!type || !id) throw new UsageError(`"${oid}" is not an OID token (Story:1234)`);
|
|
1626
|
+
const doc = await transport.request({ method: "POST", path: `/rest-1.v1/Data/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, query: { op }, responseType: "json" });
|
|
1627
|
+
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);
|
|
1628
|
+
const moment = doc.id.split(":")[2] ?? null;
|
|
1629
|
+
return { oid: momentless(doc.id), moment };
|
|
1630
|
+
}
|
|
1631
|
+
async function template(transport, type, options = {}) {
|
|
1632
|
+
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" });
|
|
1633
|
+
const { _oid: _ignored, ...values } = fromAssetDocument({ ...doc, id: doc.id || `${type}:0` });
|
|
1634
|
+
return values;
|
|
1635
|
+
}
|
|
1484
1636
|
var history = (transport, target, params) => {
|
|
1485
1637
|
const [type, id] = target.split(":");
|
|
1486
1638
|
const path = id ? `/rest-1.v1/Hist/${encodeURIComponent(type)}/${encodeURIComponent(id)}` : `/rest-1.v1/Hist/${encodeURIComponent(type)}`;
|
|
@@ -1563,11 +1715,21 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1563
1715
|
return row;
|
|
1564
1716
|
}
|
|
1565
1717
|
// ---- the endpoints behind the reads -----------------------------------------------
|
|
1566
|
-
/**
|
|
1718
|
+
/**
|
|
1719
|
+
* meta.v1: the instance's schema, raw. (The translated, cached view is
|
|
1720
|
+
* `schema`; `schema.revalidate()` drops it when the schema's tag moves.)
|
|
1721
|
+
* `read` and `readType` are conditional: pass the tag a previous answer
|
|
1722
|
+
* carried and an unchanged schema answers null without a download —
|
|
1723
|
+
* meta.v1 sends one strong ETag for the whole schema. `etag()` reads that
|
|
1724
|
+
* tag alone, from the smallest resource.
|
|
1725
|
+
*/
|
|
1567
1726
|
meta = {
|
|
1568
1727
|
metamodel: () => getMetamodel(this.transport),
|
|
1728
|
+
read: (options) => readMetamodel(this.transport, options),
|
|
1569
1729
|
assetType: (token) => getAssetType(this.transport, token),
|
|
1570
|
-
|
|
1730
|
+
readType: (token, options) => readAssetType(this.transport, token, options),
|
|
1731
|
+
attributeDefinition: (tokenref) => getAttributeDefinition(this.transport, tokenref),
|
|
1732
|
+
etag: (options) => etag(this.transport, options)
|
|
1571
1733
|
};
|
|
1572
1734
|
/**
|
|
1573
1735
|
* loc-2.v1: labels for tags, in one request (meta's DisplayName values are
|
|
@@ -1578,16 +1740,39 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1578
1740
|
return localize(this.transport, tags, options);
|
|
1579
1741
|
}
|
|
1580
1742
|
/**
|
|
1581
|
-
* rest-1.v1, for what
|
|
1582
|
-
* true`), one asset by OID without a page,
|
|
1583
|
-
* moment)
|
|
1743
|
+
* rest-1.v1, for what the other endpoints cannot do: deleted assets
|
|
1744
|
+
* (`deleted: true`), one asset by OID without a page, history (one row
|
|
1745
|
+
* per moment), a new asset's template (`template`: what the type's
|
|
1746
|
+
* initializer fills in for a context — a Timebox's next name and dates,
|
|
1747
|
+
* a Story's carry-over from a Request), and an operation's result
|
|
1748
|
+
* (`operate`: api/asset performs a Copy but never says what it made;
|
|
1749
|
+
* rest-1 answers the new OID). Parameters are spelled as a query's
|
|
1750
|
+
* (`select`, `filter`).
|
|
1584
1751
|
*/
|
|
1585
1752
|
data = {
|
|
1586
1753
|
list: (type, params) => list2(this.transport, type, params),
|
|
1587
1754
|
get: (oid, select) => get2(this.transport, oid, select),
|
|
1588
|
-
history: (target, params) => history(this.transport, target, params)
|
|
1755
|
+
history: (target, params) => history(this.transport, target, params),
|
|
1756
|
+
template: (type, options) => template(this.transport, type, options),
|
|
1757
|
+
operate: (oid, op) => operate(this.transport, oid, op)
|
|
1589
1758
|
};
|
|
1590
|
-
/**
|
|
1759
|
+
/** 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. */
|
|
1760
|
+
async copy(oid) {
|
|
1761
|
+
return (await operate(this.transport, oid, "Copy")).oid;
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* What a form needs for an asset type in a scope: the attributes the
|
|
1765
|
+
* server requires to create one (meta), the ones the product shows there
|
|
1766
|
+
* (api/visibleFields, custom fields included once configured), and the
|
|
1767
|
+
* scope's scheme's important fields and whether they are enforced at
|
|
1768
|
+
* close. `visible` alone answers for several types and scopes at once.
|
|
1769
|
+
*/
|
|
1770
|
+
fields = {
|
|
1771
|
+
describe: (type, scope) => describe2(this.transport, this.schema, type, scope),
|
|
1772
|
+
visible: (types, scopes) => visibleFields(this.transport, types, scopes),
|
|
1773
|
+
visibleByAsset: (oid) => visibleFieldsByAsset(this.transport, oid)
|
|
1774
|
+
};
|
|
1775
|
+
/** @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. */
|
|
1591
1776
|
queryV1 = {
|
|
1592
1777
|
query: (q) => this.query(q),
|
|
1593
1778
|
queries: (qs) => this.queries(qs)
|
|
@@ -1619,7 +1804,7 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1619
1804
|
await this.schema.ensureFor(commands);
|
|
1620
1805
|
return sendGuarded(this.transport, commands, { ...options, lookup: this.schema });
|
|
1621
1806
|
},
|
|
1622
|
-
/** @deprecated since 1.0.0-alpha.3: use `client.query` (or `client.queryPage`
|
|
1807
|
+
/** @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. */
|
|
1623
1808
|
query: async (q) => {
|
|
1624
1809
|
await this.schema.ensureFor([q]);
|
|
1625
1810
|
return query(this.transport, q, { lookup: this.schema });
|
|
@@ -1679,8 +1864,19 @@ var AgilityApiClient = class _AgilityApiClient {
|
|
|
1679
1864
|
};
|
|
1680
1865
|
/** Where a browser opens things on this instance: an asset's page, an attachment's bytes, an embedded image. */
|
|
1681
1866
|
urls = {
|
|
1682
|
-
/**
|
|
1683
|
-
|
|
1867
|
+
/**
|
|
1868
|
+
* The asset's page, from an OID token or an asset Number (S-01001,
|
|
1869
|
+
* what a person typed and what a message shows): assetdetail.v1
|
|
1870
|
+
* resolves either and redirects to the type's own detail page; a signed-
|
|
1871
|
+
* out reader lands there after logging in. Anything else is a UsageError.
|
|
1872
|
+
*/
|
|
1873
|
+
asset: (ref) => {
|
|
1874
|
+
const base = this.config.baseUrl.replace(/\/+$/, "");
|
|
1875
|
+
const text = ref.trim();
|
|
1876
|
+
if (isOidToken(text)) return `${base}/assetdetail.v1?oid=${momentless(text)}`;
|
|
1877
|
+
if (isAssetNumber(text)) return `${base}/assetdetail.v1?Number=${encodeURIComponent(text)}`;
|
|
1878
|
+
throw new UsageError(`"${ref}" is neither an OID token (Story:1234) nor an asset number (S-1234)`);
|
|
1879
|
+
},
|
|
1684
1880
|
attachment: (id) => attachmentUrl(this.config.baseUrl, id),
|
|
1685
1881
|
embeddedImage: (id) => embeddedImageUrl(this.config.baseUrl, id)
|
|
1686
1882
|
};
|
|
@@ -1708,7 +1904,9 @@ export {
|
|
|
1708
1904
|
attachment_exports as attachment,
|
|
1709
1905
|
createFetchTransport,
|
|
1710
1906
|
describe as describeBatch,
|
|
1907
|
+
fields_exports as fields,
|
|
1711
1908
|
guard,
|
|
1909
|
+
importantName,
|
|
1712
1910
|
isAssetNumber,
|
|
1713
1911
|
isOidToken,
|
|
1714
1912
|
kindOf,
|