@onlyworlds/sdk 4.8.0 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +66 -63
- package/CHANGELOG.md +459 -434
- package/LICENSE +21 -21
- package/README.md +183 -167
- package/SCHEMA.md +631 -631
- package/dist/index.d.ts +36 -5
- package/dist/index.js +36 -7
- package/package.json +64 -64
package/dist/index.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ type ElementType = 'ability' | 'character' | 'collective' | 'construct' | 'creat
|
|
|
33
33
|
declare const ELEMENT_TYPES: ElementType[];
|
|
34
34
|
/** Canonical OnlyWorlds schema version. Source: the `canonical:` value of the pinned
|
|
35
35
|
* distribution's VERSION file (see the provenance block at the top of this file). */
|
|
36
|
-
declare const ONLYWORLDS_VERSION: "00.
|
|
36
|
+
declare const ONLYWORLDS_VERSION: "00.32.00";
|
|
37
37
|
/** The four semantic families (colour carries the family; ELEMENT_ICONS carries the type). */
|
|
38
38
|
type ElementFamily = 'agents' | 'world' | 'abstract' | 'temporal';
|
|
39
39
|
/** Per-type semantic family. Source: the distribution's `presentation.json` sidecar
|
|
@@ -2132,6 +2132,25 @@ declare class OwApiError extends Error {
|
|
|
2132
2132
|
* key never sees it.
|
|
2133
2133
|
*/
|
|
2134
2134
|
get isOwnerOnly(): boolean;
|
|
2135
|
+
/**
|
|
2136
|
+
* A `patch` or `editLinks` sent with `ifMatch` found the element moved past that
|
|
2137
|
+
* `change_seq` (412 `precondition_failed`, keel #210): nothing was written and nothing
|
|
2138
|
+
* merged. The error is an OwPreconditionFailedError; its `current` is the element as it
|
|
2139
|
+
* is now, so no second read is needed.
|
|
2140
|
+
*/
|
|
2141
|
+
get isPreconditionFailed(): boolean;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* 412 `precondition_failed` (keel #210): the element changed since the `change_seq` sent as
|
|
2145
|
+
* `ifMatch`. `current` is the element as it is now, as this key sees it (keel sends it beside
|
|
2146
|
+
* the error envelope; null if a response ever lacks it). The check is per element and the
|
|
2147
|
+
* server never merges: compare the copy you edited from with `current`; when the fields you
|
|
2148
|
+
* changed are not the ones that moved, re-send with `ifMatch: current.change_seq`; when the
|
|
2149
|
+
* same field moved on both sides, ask the user which to keep.
|
|
2150
|
+
*/
|
|
2151
|
+
declare class OwPreconditionFailedError extends OwApiError {
|
|
2152
|
+
readonly current: Record<string, unknown> | null;
|
|
2153
|
+
constructor(status: number, code: string | null, message: string, docUrl: string | null, detail: unknown, type?: string | null, param?: string | null, retryAfter?: number | null);
|
|
2135
2154
|
}
|
|
2136
2155
|
/** Network-level failure (fetch rejected) -- no envelope to parse. */
|
|
2137
2156
|
declare class OwNetworkError extends Error {
|
|
@@ -2227,8 +2246,15 @@ declare class OwV2Client {
|
|
|
2227
2246
|
* PATCH /{type}/{id}/ -- partial update. DESTRUCTIVE on sent fields: arrays
|
|
2228
2247
|
* replace wholesale, omitted fields stay untouched. For link arrays prefer
|
|
2229
2248
|
* editLinks() -- atomic server-side merge, no read-before-write.
|
|
2249
|
+
*
|
|
2250
|
+
* `ifMatch` (keel #210): the element's `change_seq` as you read it. The update lands
|
|
2251
|
+
* only if nobody changed the element since; otherwise nothing is written and this throws
|
|
2252
|
+
* an OwPreconditionFailedError (412) whose `current` is the element as it is now. Omit
|
|
2253
|
+
* it for today's unconditional write.
|
|
2230
2254
|
*/
|
|
2231
|
-
patch(type: ElementType | string, id: string, partial: Record<string, unknown
|
|
2255
|
+
patch(type: ElementType | string, id: string, partial: Record<string, unknown>, opts?: {
|
|
2256
|
+
ifMatch?: number;
|
|
2257
|
+
}): Promise<OwElement>;
|
|
2232
2258
|
/**
|
|
2233
2259
|
* DELETE /{type}/{id}/ -- idempotent (204 on absent). Server writes a
|
|
2234
2260
|
* tombstone AND scrubs the id from every other element's links in the same
|
|
@@ -2239,9 +2265,12 @@ declare class OwV2Client {
|
|
|
2239
2265
|
* POST /{type}/{id}/links/{field} with {add, remove} -- atomic link merge.
|
|
2240
2266
|
* Dedupes, tolerates already-present/already-absent ids. Returns the FULL
|
|
2241
2267
|
* updated element (fixture P5). Use this for all relationship editing; it
|
|
2242
|
-
* retires the read-merge-PATCH dance.
|
|
2268
|
+
* retires the read-merge-PATCH dance. `ifMatch` works as on patch(): a link write
|
|
2269
|
+
* moves the element too.
|
|
2243
2270
|
*/
|
|
2244
|
-
editLinks(type: ElementType | string, id: string, field: string, edit: OwLinkEdit
|
|
2271
|
+
editLinks(type: ElementType | string, id: string, field: string, edit: OwLinkEdit, opts?: {
|
|
2272
|
+
ifMatch?: number;
|
|
2273
|
+
}): Promise<OwElement>;
|
|
2245
2274
|
/**
|
|
2246
2275
|
* POST /bulk -- up to ~1000 items. Partial success by default (HTTP 200
|
|
2247
2276
|
* always; inspect per-slot numeric `status` + top-level `errors` flag);
|
|
@@ -2353,6 +2382,8 @@ declare class OwV2Client {
|
|
|
2353
2382
|
auth?: boolean;
|
|
2354
2383
|
allowEmpty?: boolean;
|
|
2355
2384
|
replayAware?: boolean;
|
|
2385
|
+
/** A `change_seq` to send as `If-Match` (keel #210); a whole number, else TypeError before sending. */
|
|
2386
|
+
ifMatch?: number;
|
|
2356
2387
|
}): Promise<T>;
|
|
2357
2388
|
}
|
|
2358
2389
|
|
|
@@ -2712,4 +2743,4 @@ declare class TokenResource {
|
|
|
2712
2743
|
getEncryptionInfo(): Promise<EncryptionInfo>;
|
|
2713
2744
|
}
|
|
2714
2745
|
|
|
2715
|
-
export { type AccessKeyResponse, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, FAMILY_COLORS, FAMILY_ORDER, FIELD_SCHEMA, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, OwApiError, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwChange, type OwChangesPage, type OwClientConfig, type OwElement, type OwElementBase, type OwErrorBody, type OwKeyKind, type OwLinkEdit, type OwMediaTicket, OwNetworkError, type OwPage, type OwRemovalTicket, type OwRemovedImage, type OwUploadedImage, OwV2Client, type OwWorldMeta, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, detectKeyKind, elementColor, errorFromResponse, familyOf, getElementIcon, getElementLabel, isDemoKey, kindCanWrite, parseErrorEnvelope, pinExpectation };
|
|
2746
|
+
export { type AccessKeyResponse, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, FAMILY_COLORS, FAMILY_ORDER, FIELD_SCHEMA, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, OwApiError, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwChange, type OwChangesPage, type OwClientConfig, type OwElement, type OwElementBase, type OwErrorBody, type OwKeyKind, type OwLinkEdit, type OwMediaTicket, OwNetworkError, type OwPage, OwPreconditionFailedError, type OwRemovalTicket, type OwRemovedImage, type OwUploadedImage, OwV2Client, type OwWorldMeta, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, detectKeyKind, elementColor, errorFromResponse, familyOf, getElementIcon, getElementLabel, isDemoKey, kindCanWrite, parseErrorEnvelope, pinExpectation };
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,23 @@ var OwApiError = class extends Error {
|
|
|
66
66
|
get isOwnerOnly() {
|
|
67
67
|
return this.status === 403 && this.code === "owner_only";
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* A `patch` or `editLinks` sent with `ifMatch` found the element moved past that
|
|
71
|
+
* `change_seq` (412 `precondition_failed`, keel #210): nothing was written and nothing
|
|
72
|
+
* merged. The error is an OwPreconditionFailedError; its `current` is the element as it
|
|
73
|
+
* is now, so no second read is needed.
|
|
74
|
+
*/
|
|
75
|
+
get isPreconditionFailed() {
|
|
76
|
+
return this.status === 412 && this.code === "precondition_failed";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var OwPreconditionFailedError = class extends OwApiError {
|
|
80
|
+
constructor(status, code, message, docUrl, detail, type = null, param = null, retryAfter = null) {
|
|
81
|
+
super(status, code, message, docUrl, detail, type, param, retryAfter);
|
|
82
|
+
this.name = "OwPreconditionFailedError";
|
|
83
|
+
const cur = detail && typeof detail === "object" ? detail.current : void 0;
|
|
84
|
+
this.current = cur && typeof cur === "object" ? cur : null;
|
|
85
|
+
}
|
|
69
86
|
};
|
|
70
87
|
var OwNetworkError = class extends Error {
|
|
71
88
|
constructor(message, cause) {
|
|
@@ -82,7 +99,8 @@ function parseErrorEnvelope(status, body, retryAfter = null) {
|
|
|
82
99
|
const param = env.param ?? nested?.param ?? null;
|
|
83
100
|
const docUrl = env.doc_url ?? nested?.doc_url ?? null;
|
|
84
101
|
const message = env.message ?? nested?.message ?? (typeof env.detail === "string" ? env.detail : void 0) ?? `OnlyWorlds API error ${status}${code ? ` (${code})` : ""}`;
|
|
85
|
-
|
|
102
|
+
const Cls = status === 412 && code === "precondition_failed" ? OwPreconditionFailedError : OwApiError;
|
|
103
|
+
return new Cls(status, code, message, docUrl, body, type, param, retryAfter);
|
|
86
104
|
}
|
|
87
105
|
function parseRetryAfter(value, now = Date.now()) {
|
|
88
106
|
if (value == null) return null;
|
|
@@ -221,9 +239,14 @@ var OwV2Client = class {
|
|
|
221
239
|
* PATCH /{type}/{id}/ -- partial update. DESTRUCTIVE on sent fields: arrays
|
|
222
240
|
* replace wholesale, omitted fields stay untouched. For link arrays prefer
|
|
223
241
|
* editLinks() -- atomic server-side merge, no read-before-write.
|
|
242
|
+
*
|
|
243
|
+
* `ifMatch` (keel #210): the element's `change_seq` as you read it. The update lands
|
|
244
|
+
* only if nobody changed the element since; otherwise nothing is written and this throws
|
|
245
|
+
* an OwPreconditionFailedError (412) whose `current` is the element as it is now. Omit
|
|
246
|
+
* it for today's unconditional write.
|
|
224
247
|
*/
|
|
225
|
-
async patch(type, id, partial) {
|
|
226
|
-
return this.request("PATCH", `/${type}/${id}/`, { body: sanitizePayload(partial) });
|
|
248
|
+
async patch(type, id, partial, opts = {}) {
|
|
249
|
+
return this.request("PATCH", `/${type}/${id}/`, { body: sanitizePayload(partial), ifMatch: opts.ifMatch });
|
|
227
250
|
}
|
|
228
251
|
/**
|
|
229
252
|
* DELETE /{type}/{id}/ -- idempotent (204 on absent). Server writes a
|
|
@@ -237,10 +260,11 @@ var OwV2Client = class {
|
|
|
237
260
|
* POST /{type}/{id}/links/{field} with {add, remove} -- atomic link merge.
|
|
238
261
|
* Dedupes, tolerates already-present/already-absent ids. Returns the FULL
|
|
239
262
|
* updated element (fixture P5). Use this for all relationship editing; it
|
|
240
|
-
* retires the read-merge-PATCH dance.
|
|
263
|
+
* retires the read-merge-PATCH dance. `ifMatch` works as on patch(): a link write
|
|
264
|
+
* moves the element too.
|
|
241
265
|
*/
|
|
242
|
-
async editLinks(type, id, field, edit) {
|
|
243
|
-
return this.request("POST", `/${type}/${id}/links/${field}`, { body: edit });
|
|
266
|
+
async editLinks(type, id, field, edit, opts = {}) {
|
|
267
|
+
return this.request("POST", `/${type}/${id}/links/${field}`, { body: edit, ifMatch: opts.ifMatch });
|
|
244
268
|
}
|
|
245
269
|
// -- Bulk ----------------------------------------------------------------
|
|
246
270
|
/**
|
|
@@ -395,6 +419,9 @@ var OwV2Client = class {
|
|
|
395
419
|
* sanitizePayload — callers own their body shape.
|
|
396
420
|
*/
|
|
397
421
|
async request(method, path, opts = {}) {
|
|
422
|
+
if (opts.ifMatch !== void 0 && !(Number.isInteger(opts.ifMatch) && opts.ifMatch >= 0)) {
|
|
423
|
+
throw new TypeError(`ifMatch must be a whole number (an element's change_seq), got ${String(opts.ifMatch)}`);
|
|
424
|
+
}
|
|
398
425
|
const url = `${this.baseUrl}${path}${opts.query ? `?${opts.query}` : ""}`;
|
|
399
426
|
const headers = {};
|
|
400
427
|
if (opts.auth !== false) {
|
|
@@ -407,6 +434,7 @@ var OwV2Client = class {
|
|
|
407
434
|
}
|
|
408
435
|
if (opts.body !== void 0) headers["Content-Type"] = "application/json";
|
|
409
436
|
if (opts.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
437
|
+
if (opts.ifMatch !== void 0) headers["If-Match"] = `"${opts.ifMatch}"`;
|
|
410
438
|
let res;
|
|
411
439
|
try {
|
|
412
440
|
res = await this.fetchImpl(url, {
|
|
@@ -475,7 +503,7 @@ function buildQuery(params) {
|
|
|
475
503
|
|
|
476
504
|
// src/v2/types.generated.ts
|
|
477
505
|
var ELEMENT_TYPES = ["ability", "character", "collective", "construct", "creature", "event", "family", "institution", "language", "law", "location", "map", "marker", "narrative", "object", "phenomenon", "pin", "relation", "species", "title", "trait", "zone"];
|
|
478
|
-
var ONLYWORLDS_VERSION = "00.
|
|
506
|
+
var ONLYWORLDS_VERSION = "00.32.00";
|
|
479
507
|
var ELEMENT_FAMILIES = {
|
|
480
508
|
ability: "abstract",
|
|
481
509
|
character: "agents",
|
|
@@ -1488,6 +1516,7 @@ export {
|
|
|
1488
1516
|
ONLYWORLDS_VERSION,
|
|
1489
1517
|
OwApiError,
|
|
1490
1518
|
OwNetworkError,
|
|
1519
|
+
OwPreconditionFailedError,
|
|
1491
1520
|
OwV2Client,
|
|
1492
1521
|
SPATIAL_TYPES,
|
|
1493
1522
|
TokenResource,
|
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@onlyworlds/sdk",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": {
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"./package.json": "./package.json"
|
|
12
|
-
},
|
|
13
|
-
"types": "dist/index.d.ts",
|
|
14
|
-
"sideEffects": false,
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"README.md",
|
|
18
|
-
"AGENTS.md",
|
|
19
|
-
"CHANGELOG.md",
|
|
20
|
-
"SCHEMA.md"
|
|
21
|
-
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
24
|
-
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
25
|
-
"pretest": "npm run build",
|
|
26
|
-
"test": "node --test",
|
|
27
|
-
"codegen": "python codegen/generate_types.py",
|
|
28
|
-
"codegen:check": "python codegen/generate_types.py --check",
|
|
29
|
-
"schema:verify": "python codegen/verify_dist.py",
|
|
30
|
-
"schema:check": "npm run schema:verify && npm run codegen:check",
|
|
31
|
-
"prepublishOnly": "npm run build",
|
|
32
|
-
"release:verify": "node codegen/verify_release.mjs"
|
|
33
|
-
},
|
|
34
|
-
"keywords": [
|
|
35
|
-
"onlyworlds",
|
|
36
|
-
"worldbuilding",
|
|
37
|
-
"api",
|
|
38
|
-
"sdk",
|
|
39
|
-
"typescript",
|
|
40
|
-
"rpg",
|
|
41
|
-
"ttrpg",
|
|
42
|
-
"game-development"
|
|
43
|
-
],
|
|
44
|
-
"author": "OnlyWorlds",
|
|
45
|
-
"license": "MIT",
|
|
46
|
-
"repository": {
|
|
47
|
-
"type": "git",
|
|
48
|
-
"url": "git+https://github.com/OnlyWorlds/sdk.git"
|
|
49
|
-
},
|
|
50
|
-
"homepage": "https://onlyworlds.github.io/",
|
|
51
|
-
"bugs": {
|
|
52
|
-
"url": "https://github.com/OnlyWorlds/sdk/issues"
|
|
53
|
-
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"tsup": "^8.0.1",
|
|
56
|
-
"typescript": "^5.3.3"
|
|
57
|
-
},
|
|
58
|
-
"peerDependencies": {
|
|
59
|
-
"typescript": ">=4.5.0"
|
|
60
|
-
},
|
|
61
|
-
"engines": {
|
|
62
|
-
"node": ">=18.0.0"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@onlyworlds/sdk",
|
|
3
|
+
"version": "4.9.0",
|
|
4
|
+
"description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"AGENTS.md",
|
|
19
|
+
"CHANGELOG.md",
|
|
20
|
+
"SCHEMA.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
24
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
25
|
+
"pretest": "npm run build",
|
|
26
|
+
"test": "node --test",
|
|
27
|
+
"codegen": "python codegen/generate_types.py",
|
|
28
|
+
"codegen:check": "python codegen/generate_types.py --check",
|
|
29
|
+
"schema:verify": "python codegen/verify_dist.py",
|
|
30
|
+
"schema:check": "npm run schema:verify && npm run codegen:check",
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"release:verify": "node codegen/verify_release.mjs"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"onlyworlds",
|
|
36
|
+
"worldbuilding",
|
|
37
|
+
"api",
|
|
38
|
+
"sdk",
|
|
39
|
+
"typescript",
|
|
40
|
+
"rpg",
|
|
41
|
+
"ttrpg",
|
|
42
|
+
"game-development"
|
|
43
|
+
],
|
|
44
|
+
"author": "OnlyWorlds",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/OnlyWorlds/sdk.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://onlyworlds.github.io/",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/OnlyWorlds/sdk/issues"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"tsup": "^8.0.1",
|
|
56
|
+
"typescript": "^5.3.3"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"typescript": ">=4.5.0"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|