@onlyworlds/sdk 4.1.0 → 4.3.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 +54 -34
- package/CHANGELOG.md +84 -0
- package/SCHEMA.md +4 -2
- package/dist/index.d.ts +37 -7
- package/dist/index.js +48 -13
- package/package.json +3 -2
package/AGENTS.md
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
|
-
# For AI agents using @onlyworlds/sdk
|
|
2
|
-
|
|
3
|
-
**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
# For AI agents using @onlyworlds/sdk
|
|
2
|
+
|
|
3
|
+
**Current as of**: SDK **4.x** · schema-dist **v0.30.1-dist.15** (canonical 00.30.01).
|
|
4
|
+
This line is asserted by `codegen:check` in CI — if the pin moves and this file is not
|
|
5
|
+
re-read against it, the check fails rather than letting this document rot quietly.
|
|
6
|
+
|
|
7
|
+
**Start with [SCHEMA.md](SCHEMA.md)** (in this package): the full generated schema reference —
|
|
8
|
+
every type, every field with its meaning, link directions, families, icons, display sections.
|
|
9
|
+
It is generated from the same canonical YAML as the types, so it cannot drift.
|
|
10
|
+
|
|
11
|
+
**What this package is**: the canonical typed TypeScript client for the OnlyWorlds v2 API,
|
|
12
|
+
plus the canonical constants (element types, icons, colour families, field schema).
|
|
13
|
+
OnlyWorlds is an open standard for portable world data — 22 element types, UUID-linked.
|
|
14
|
+
|
|
15
|
+
**Use the v2 surface.** `OwV2Client` + the `V2ElementType` slug union + the generated
|
|
16
|
+
interfaces in `types.generated.ts` (emitted from the canonical schema YAML, validated
|
|
17
|
+
against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum) is not in
|
|
18
|
+
4.x — it was removed at 4.0.0 and lives only in 3.x. Do not build new work on it.
|
|
19
|
+
|
|
20
|
+
**SDK vs MCP server — pick correctly**:
|
|
21
|
+
- Known, deterministic operations (CRUD, sync, bulk) → **this SDK**. Typed calls, typed
|
|
22
|
+
responses, far cheaper than tool-schema reasoning.
|
|
23
|
+
- Live exploration of a user's world from a chat/agent context → the **MCP server** at
|
|
24
|
+
`https://www.onlyworlds.com/mcp` (same `API-Key`/`API-Pin` headers, 11 tools).
|
|
25
|
+
|
|
26
|
+
**Wire facts that bite** (full details in README):
|
|
27
|
+
- Never send a `"world"` field in payloads — world identity comes from the API key (422 otherwise).
|
|
28
|
+
- v2 link fields use ONE name both directions (no `_ids` suffix — that is v1 dialect only).
|
|
29
|
+
- PATCH is destructive on sent fields; use `editLinks` (atomic add/remove) for relationships.
|
|
30
|
+
- World-meta changes do NOT appear in `/changes` — poll `GET /world` separately.
|
|
31
|
+
- Extension fields: `x_<toolname>_*` is the sanctioned namespace for tool-specific state;
|
|
32
|
+
unknown unprefixed fields 422. Extensions are capped at **64 KB per element** (422,
|
|
33
|
+
`param: extensions`).
|
|
34
|
+
- List filters: only `name__icontains`, `supertype` and `subtype` are built. Any other
|
|
35
|
+
filter key — and `?ordering=` — returns 422 naming it. Filter or sort client-side.
|
|
36
|
+
- Ids: the client mints **UUIDv7** on an id-less `create` (since 4.2.0; keel mints v7 too).
|
|
37
|
+
A v4 or v7 id you supply is accepted. **Never sort elements by id**: worlds mix v7, v4
|
|
38
|
+
and legacy `06x…` ids (nibble 7 too, but seconds-first). For creation order use
|
|
39
|
+
`created_at`; `change_seq` is last-write order, not creation.
|
|
40
|
+
A create with an id that already exists, or a PUT whose id belongs to **another world**,
|
|
41
|
+
returns 409 `id_conflict` (`err.isIdConflict`; retrying won't help). `/bulk` never throws:
|
|
42
|
+
check each slot for `status: 409` with `error.code: 'id_conflict'`. A different 409, `idempotency_error`, means an
|
|
43
|
+
Idempotency-Key was reused with another body (`err.isIdempotencyConflict`).
|
|
44
|
+
- Under load keel answers 503 `server_busy` with `Retry-After` (`err.isBusy`, `err.retryAfter`
|
|
45
|
+
in seconds). The client does not retry for you; back off and retry yourself. Works the same
|
|
46
|
+
in browsers.
|
|
47
|
+
- A string holding an unpaired surrogate (text cut mid-emoji) is a 422 naming the field.
|
|
48
|
+
Slice strings by code point, not by UTF-16 unit.
|
|
49
|
+
- Colour carries the element's FAMILY (`elementColor(type, mode)`); the icon
|
|
50
|
+
(`ELEMENT_ICONS`) carries the TYPE. Icon + label are required alongside colour, not optional.
|
|
51
|
+
|
|
52
|
+
**Auth**: prefixed keys — `ow_w_` (read+write), `ow_r_` (read-only, no PIN — the share
|
|
53
|
+
primitive), `ow_a_` (account Bearer). Demo keys `0000000000`–`0000000009` are read-only
|
|
54
|
+
test credentials against real data.
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,90 @@
|
|
|
3
3
|
All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward (Kael, Assembly);
|
|
4
4
|
earlier history lives in git log only.
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [4.3.0] — 2026-09-28
|
|
9
|
+
|
|
10
|
+
Errors you can act on: the two 409s told apart, and the busy server's `Retry-After`, in Node and
|
|
11
|
+
browsers. One getter narrows (see Fixed); nothing is removed.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **`OwApiError.isIdempotencyConflict` no longer claims every 409.** keel sends two: 409
|
|
15
|
+
`idempotency_error` (a key reused with a different body) and 409 `id_conflict` (the id is
|
|
16
|
+
taken: a create with an existing id since D39, or a PUT / bulk id held by another world since
|
|
17
|
+
D70). The getter was true for both, so an id conflict read as a key problem, and retrying with
|
|
18
|
+
a fresh key could never help. It now matches `idempotency_error` only, and the new
|
|
19
|
+
**`isIdConflict`** names the other. A caller who relied on the old catch-all for `id_conflict`
|
|
20
|
+
should switch to `isIdConflict`. A 409 with no code (a proxy, a non-JSON body) now reads
|
|
21
|
+
false for both. `/bulk` is unaffected: it answers 200 and reports a per-item 409 in the slot.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **`OwApiError.retryAfter`** (seconds, or null) is parsed from `Retry-After`, as a delay or an
|
|
25
|
+
HTTP date, and **`isBusy`** marks keel's 503 `server_busy` (admission control, keel D71: six
|
|
26
|
+
requests at once per worker, up to 10 s of queueing, then 503 with `Retry-After: 5`). The client
|
|
27
|
+
still never retries on its own; it gives callers what they need to back off. A malformed
|
|
28
|
+
`Retry-After` is null, never "retry now". **Works in browsers too** since keel `4949406`
|
|
29
|
+
(2026-09-28): every response exposes `Retry-After`, `Idempotent-Replay` and
|
|
30
|
+
`X-OW-Schema-Version` over CORS (probed from an allowed origin), and keel reports that the
|
|
31
|
+
admission gate's 503 now carries CORS headers, so a busy server reads as busy, not as
|
|
32
|
+
`OwNetworkError` (keel's word; the 503 can't be triggered on demand to probe). Six tests; the
|
|
33
|
+
four core ones watched failing on the previous `errors.ts`.
|
|
34
|
+
|
|
35
|
+
## [4.2.0] — 2026-09-28
|
|
36
|
+
|
|
37
|
+
Ids, honest filter docs, and the schema repin. Nothing is removed; no call needs to change.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- **`create()` now mints an RFC 9562 UUIDv7** when the element has no id (was v4). The first
|
|
41
|
+
48 bits are the creation millisecond, so ids this client mints a millisecond or more apart
|
|
42
|
+
sort by creation (while the clock does not step backwards), which gives databases better
|
|
43
|
+
index locality. **This holds within one client only**: worlds also hold v4 ids and legacy
|
|
44
|
+
v1-server ids (`06x…`, which also carry version nibble 7 but put seconds first), so never
|
|
45
|
+
order elements by id. For creation order use `created_at`; `change_seq` is last-write
|
|
46
|
+
order. This is a **default, not a
|
|
47
|
+
requirement**: a v4 or v7 id a caller supplies is still accepted as-is, and
|
|
48
|
+
every id already stored stays valid. Keel mints v7 server-side too (Captain's ruling,
|
|
49
|
+
2026-09-28). Pinned by six tests: version and variant, the big-endian timestamp above
|
|
50
|
+
bit 32, fractional and pre-1970 clocks floored consistently, creation order across 50
|
|
51
|
+
consecutive milliseconds, 1,000 distinct ids inside one millisecond, and the no-`crypto`
|
|
52
|
+
fallback (asserting `Math.random` is actually used). Five injected defects (v4 nibble,
|
|
53
|
+
32-bit truncation, a dropped random fill, a dropped variant mask, an unfloored clock)
|
|
54
|
+
each fail the suite on every run, on Node 20 and 22. An independent reviewer's decoder agreed on 20,000 random timestamps and the 48-bit
|
|
55
|
+
edges, and keel's `uuid7()` agrees on timestamp, version and variant for a pinned clock.
|
|
56
|
+
- **`elementColor()` on an unknown type now throws a `TypeError` that names the type.** It
|
|
57
|
+
used to crash with `Cannot read properties of undefined (reading 'dark')`. Found while
|
|
58
|
+
building the Forge's colour gate: atlas's own copy falls back to the `world` family for an
|
|
59
|
+
unknown type, so swapping it for this export is not a pure re-export for that input, and the
|
|
60
|
+
docstring now says so. Whether the package should fall back is left to its consumers.
|
|
61
|
+
- The generated field-schema comment no longer calls `maximum:` an open question or counts
|
|
62
|
+
its occurrences (the count was 41; since 00.30.01 it is 15). The question was ruled on
|
|
63
|
+
2026-07-29.
|
|
64
|
+
- **`ListParams.filter` JSDoc names what the server actually accepts**: `name__icontains`,
|
|
65
|
+
`supertype`, `subtype`. It used to list `__in`, `__gte`, `__lte` and `__isnull`, copied
|
|
66
|
+
from keel's spec, which described them but never built them. Probed live 2026-09-28: the
|
|
67
|
+
three accepted keys answer 200; every other one, and `?ordering=`, answers 422.
|
|
68
|
+
- **Schema repinned `v0.30.1-dist.13` → `v0.30.1-dist.15`** (canonical unchanged, 00.30.01).
|
|
69
|
+
Two generated values move: `ELEMENT_ICONS.institution` `business` → `account_balance`,
|
|
70
|
+
`ELEMENT_ICONS.marker` `place` → `location_on` (both Material Symbols names). dist.15
|
|
71
|
+
also adds `minimum: 0` to `ability.potency`; the walk does not surface bounds, so nothing
|
|
72
|
+
generated changes for it. 31/31 file hashes recomputed from a fresh download.
|
|
73
|
+
- **`AGENTS.md` re-read against the new pin** (its gate fired on the repin, as designed).
|
|
74
|
+
It still called the v1 client "frozen legacy" in this package; v1 was removed at 4.0.0.
|
|
75
|
+
It now also names the wire behaviours keel deployed on 2026-09-28 (keel D70): the 64 KB
|
|
76
|
+
extension cap, the three built filters and the `?ordering=` 422, 409 `id_conflict` for a
|
|
77
|
+
PUT or bulk id that belongs to another world, and the 422 for unpaired surrogates. None of
|
|
78
|
+
them needs client code.
|
|
79
|
+
|
|
80
|
+
### Also in this release (staged earlier)
|
|
81
|
+
- **`SCHEMA.md` now opens with its own provenance** (dist tag, canonical version, publish
|
|
82
|
+
date — rendered from `schema-pin.json`, never the wall clock). It ships in the tarball
|
|
83
|
+
and is read cold by agents outside this repo, where the pin file is not present; a
|
|
84
|
+
generated reference that cannot name its source was the one remaining self-dating gap.
|
|
85
|
+
- **`AGENTS.md` carries a "Current as of" line, asserted by `codegen:check`** — the tag it
|
|
86
|
+
names must match the pin or CI fails. A hand-maintained agent doc with an ungated date
|
|
87
|
+
is how this repo's previous agent doc went a major version stale. Gate watched firing
|
|
88
|
+
(tampered tag → exit 1, restored → 0).
|
|
89
|
+
|
|
6
90
|
## [4.1.0] — 2026-07-29
|
|
7
91
|
|
|
8
92
|
Public-surface hygiene. Nothing breaks; one member is now marked for removal, and one
|
package/SCHEMA.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# OnlyWorlds Schema Reference
|
|
2
2
|
|
|
3
|
+
**Source**: https://github.com/OnlyWorlds/schema-dist @ **v0.30.1-dist.15** — canonical schema **00.30.01**, published 2026-09-18.
|
|
4
|
+
|
|
3
5
|
GENERATED from the canonical schema YAML — do not hand-edit (regenerate: `python codegen/generate_types.py`).
|
|
4
6
|
Written for both humans and AI agents reading this package locally.
|
|
5
7
|
|
|
@@ -244,7 +246,7 @@ Families (colour semantics; icon carries the type): agents · world · abstract
|
|
|
244
246
|
- `creatures` (multi link → creature) — Creatures owned, bonded to, or representing the family
|
|
245
247
|
|
|
246
248
|
|
|
247
|
-
## institution · family: agents · icon:
|
|
249
|
+
## institution · family: agents · icon: account_balance
|
|
248
250
|
|
|
249
251
|
|
|
250
252
|
### Foundation
|
|
@@ -376,7 +378,7 @@ Families (colour semantics; icon carries the type): agents · world · abstract
|
|
|
376
378
|
- `location` (single link → location) — Location element that this map represents
|
|
377
379
|
|
|
378
380
|
|
|
379
|
-
## marker · family: world · icon:
|
|
381
|
+
## marker · family: world · icon: location_on
|
|
380
382
|
|
|
381
383
|
|
|
382
384
|
### Details
|
package/dist/index.d.ts
CHANGED
|
@@ -1958,9 +1958,10 @@ interface ListParams {
|
|
|
1958
1958
|
/** Sparse include-set of field names. */
|
|
1959
1959
|
fields?: string[];
|
|
1960
1960
|
/**
|
|
1961
|
-
*
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
1961
|
+
* Accepted: `name__icontains`, `supertype`, `subtype`. Any other key 422s
|
|
1962
|
+
* server-side, which names the typo -- the client passes keys through
|
|
1963
|
+
* unchecked and lets the platform say so. (`__in`, `__gte`, `__lte` and
|
|
1964
|
+
* `__isnull` are designed in keel's spec but not built; `ordering` 422s too.)
|
|
1964
1965
|
*/
|
|
1965
1966
|
filter?: Record<string, string | number | boolean>;
|
|
1966
1967
|
}
|
|
@@ -2013,12 +2014,34 @@ declare class OwApiError extends Error {
|
|
|
2013
2014
|
readonly docUrl: string | null;
|
|
2014
2015
|
/** Raw parsed envelope (or body text when the body wasn't JSON). */
|
|
2015
2016
|
readonly detail: unknown;
|
|
2016
|
-
|
|
2017
|
+
/**
|
|
2018
|
+
* Seconds to wait before retrying, from the `Retry-After` header (a delay in
|
|
2019
|
+
* seconds or an HTTP date), else null. keel sends it on 503 `server_busy`
|
|
2020
|
+
* (admission control, D71) and 429 `rate_limited`. This client never retries
|
|
2021
|
+
* on its own; the value is here so callers can back off politely.
|
|
2022
|
+
* Readable in browsers too: keel exposes `Retry-After` over CORS and its
|
|
2023
|
+
* admission gate's 503 carries CORS headers (keel `4949406`, 2026-09-28).
|
|
2024
|
+
*/
|
|
2025
|
+
readonly retryAfter: number | null;
|
|
2026
|
+
constructor(status: number, code: string | null, message: string, docUrl: string | null, detail: unknown, type?: string | null, param?: string | null, retryAfter?: number | null);
|
|
2017
2027
|
get isAuthError(): boolean;
|
|
2018
2028
|
/** 422s/400s name the offending param/field -- typos error loudly platform-wide. */
|
|
2019
2029
|
get isValidationError(): boolean;
|
|
2020
|
-
/**
|
|
2030
|
+
/**
|
|
2031
|
+
* Same Idempotency-Key replayed with a different payload (409 `idempotency_error`).
|
|
2032
|
+
* Through 4.2.0 this was true for ANY 409, which misread an id conflict as a key
|
|
2033
|
+
* conflict: keel also answers 409 `id_conflict` (see isIdConflict).
|
|
2034
|
+
*/
|
|
2021
2035
|
get isIdempotencyConflict(): boolean;
|
|
2036
|
+
/**
|
|
2037
|
+
* The id is already taken (409 `id_conflict`): a create with an id that exists
|
|
2038
|
+
* (keel D39), or a PUT whose id belongs to another world (D70). Retrying will not
|
|
2039
|
+
* help; the id is the problem. `/bulk` never throws for this: it answers 200 with a
|
|
2040
|
+
* per-item slot `{status: 409, error: {code: 'id_conflict'}}` — check the slot.
|
|
2041
|
+
*/
|
|
2042
|
+
get isIdConflict(): boolean;
|
|
2043
|
+
/** keel's admission control turned the request away (503 `server_busy`); see retryAfter. */
|
|
2044
|
+
get isBusy(): boolean;
|
|
2022
2045
|
}
|
|
2023
2046
|
/** Network-level failure (fetch rejected) -- no envelope to parse. */
|
|
2024
2047
|
declare class OwNetworkError extends Error {
|
|
@@ -2028,7 +2051,7 @@ declare class OwNetworkError extends Error {
|
|
|
2028
2051
|
/** Parse a wire envelope into OwApiError parts (exported for the error type-tests). */
|
|
2029
2052
|
/** Parse the platform ERROR envelope into an OwApiError. (Renamed from parseEnvelope in 4.0 —
|
|
2030
2053
|
* distinct from the world-export envelope, which is a different artifact entirely.) */
|
|
2031
|
-
declare function parseErrorEnvelope(status: number, body: unknown): OwApiError;
|
|
2054
|
+
declare function parseErrorEnvelope(status: number, body: unknown, retryAfter?: number | null): OwApiError;
|
|
2032
2055
|
/** Build an OwApiError from a non-2xx response, tolerating non-JSON bodies. */
|
|
2033
2056
|
declare function errorFromResponse(res: Response): Promise<OwApiError>;
|
|
2034
2057
|
|
|
@@ -2099,7 +2122,7 @@ declare class OwV2Client {
|
|
|
2099
2122
|
/** GET /{type}/{id}/ -- optional one-level stub expansion / sparse fields. */
|
|
2100
2123
|
get(type: ElementType | string, id: string, opts?: Pick<ListParams, 'expand' | 'fields'>): Promise<OwElement>;
|
|
2101
2124
|
/**
|
|
2102
|
-
* POST /{type}/ -- create. Mints an RFC
|
|
2125
|
+
* POST /{type}/ -- create. Mints an RFC 9562 UUIDv7 for element.id when the
|
|
2103
2126
|
* caller omits one (design ruling D29d) so a retry carrying the same
|
|
2104
2127
|
* Idempotency-Key is structurally safe. Callers MAY still supply their own id.
|
|
2105
2128
|
*/
|
|
@@ -2223,6 +2246,13 @@ declare function familyOf(type: ElementType): ElementFamily;
|
|
|
2223
2246
|
* Name matches the live atlas/council implementations so their SDK swap is a
|
|
2224
2247
|
* re-export, not a rename. Defaults to `dark` (both current consumers are
|
|
2225
2248
|
* dark-surface).
|
|
2249
|
+
*
|
|
2250
|
+
* An unknown type throws a TypeError naming it (since 4.2.0; before, it crashed
|
|
2251
|
+
* with "Cannot read properties of undefined"). This is the one place a swap from
|
|
2252
|
+
* atlas's own copy is NOT a pure re-export: atlas falls back to the `world`
|
|
2253
|
+
* family for an unknown type. A caller that wants a fallback catches, or checks
|
|
2254
|
+
* `type in ELEMENT_FAMILIES` first; which fallback, if any, is a design call
|
|
2255
|
+
* this package does not make for its consumers.
|
|
2226
2256
|
*/
|
|
2227
2257
|
declare function elementColor(type: ElementType, mode?: 'light' | 'dark'): string;
|
|
2228
2258
|
/** All four families, in ruling order (the order IS the CVD-safety mechanism of the source palette). */
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/v2/errors.ts
|
|
2
2
|
var OwApiError = class extends Error {
|
|
3
|
-
constructor(status, code, message, docUrl, detail, type = null, param = null) {
|
|
3
|
+
constructor(status, code, message, docUrl, detail, type = null, param = null, retryAfter = null) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.name = "OwApiError";
|
|
6
6
|
this.status = status;
|
|
@@ -9,6 +9,7 @@ var OwApiError = class extends Error {
|
|
|
9
9
|
this.param = param;
|
|
10
10
|
this.docUrl = docUrl;
|
|
11
11
|
this.detail = detail;
|
|
12
|
+
this.retryAfter = retryAfter;
|
|
12
13
|
}
|
|
13
14
|
get isAuthError() {
|
|
14
15
|
return this.code === "invalid_credentials" || this.code === "key_revoked" || this.code === "world_gone";
|
|
@@ -17,9 +18,26 @@ var OwApiError = class extends Error {
|
|
|
17
18
|
get isValidationError() {
|
|
18
19
|
return this.status === 422 || this.status === 400;
|
|
19
20
|
}
|
|
20
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Same Idempotency-Key replayed with a different payload (409 `idempotency_error`).
|
|
23
|
+
* Through 4.2.0 this was true for ANY 409, which misread an id conflict as a key
|
|
24
|
+
* conflict: keel also answers 409 `id_conflict` (see isIdConflict).
|
|
25
|
+
*/
|
|
21
26
|
get isIdempotencyConflict() {
|
|
22
|
-
return this.status === 409;
|
|
27
|
+
return this.status === 409 && this.code === "idempotency_error";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The id is already taken (409 `id_conflict`): a create with an id that exists
|
|
31
|
+
* (keel D39), or a PUT whose id belongs to another world (D70). Retrying will not
|
|
32
|
+
* help; the id is the problem. `/bulk` never throws for this: it answers 200 with a
|
|
33
|
+
* per-item slot `{status: 409, error: {code: 'id_conflict'}}` — check the slot.
|
|
34
|
+
*/
|
|
35
|
+
get isIdConflict() {
|
|
36
|
+
return this.status === 409 && this.code === "id_conflict";
|
|
37
|
+
}
|
|
38
|
+
/** keel's admission control turned the request away (503 `server_busy`); see retryAfter. */
|
|
39
|
+
get isBusy() {
|
|
40
|
+
return this.status === 503 && this.code === "server_busy";
|
|
23
41
|
}
|
|
24
42
|
};
|
|
25
43
|
var OwNetworkError = class extends Error {
|
|
@@ -29,7 +47,7 @@ var OwNetworkError = class extends Error {
|
|
|
29
47
|
this.cause2 = cause;
|
|
30
48
|
}
|
|
31
49
|
};
|
|
32
|
-
function parseErrorEnvelope(status, body) {
|
|
50
|
+
function parseErrorEnvelope(status, body, retryAfter = null) {
|
|
33
51
|
const env = body && typeof body === "object" ? body : {};
|
|
34
52
|
const nested = typeof env.error === "object" && env.error !== null ? env.error : void 0;
|
|
35
53
|
const code = env.code ?? nested?.code ?? (typeof env.error === "string" ? env.error : null) ?? null;
|
|
@@ -37,7 +55,16 @@ function parseErrorEnvelope(status, body) {
|
|
|
37
55
|
const param = env.param ?? nested?.param ?? null;
|
|
38
56
|
const docUrl = env.doc_url ?? nested?.doc_url ?? null;
|
|
39
57
|
const message = env.message ?? nested?.message ?? (typeof env.detail === "string" ? env.detail : void 0) ?? `OnlyWorlds API error ${status}${code ? ` (${code})` : ""}`;
|
|
40
|
-
return new OwApiError(status, code, message, docUrl, body, type, param);
|
|
58
|
+
return new OwApiError(status, code, message, docUrl, body, type, param, retryAfter);
|
|
59
|
+
}
|
|
60
|
+
function parseRetryAfter(value, now = Date.now()) {
|
|
61
|
+
if (value == null) return null;
|
|
62
|
+
const v = value.trim();
|
|
63
|
+
if (/^\d+$/.test(v)) return Number(v);
|
|
64
|
+
if (!/[A-Za-z]/.test(v)) return null;
|
|
65
|
+
const when = Date.parse(v);
|
|
66
|
+
if (Number.isNaN(when)) return null;
|
|
67
|
+
return Math.max(0, Math.ceil((when - now) / 1e3));
|
|
41
68
|
}
|
|
42
69
|
async function errorFromResponse(res) {
|
|
43
70
|
let body = null;
|
|
@@ -48,7 +75,7 @@ async function errorFromResponse(res) {
|
|
|
48
75
|
} catch {
|
|
49
76
|
body = text || null;
|
|
50
77
|
}
|
|
51
|
-
return parseErrorEnvelope(res.status, body);
|
|
78
|
+
return parseErrorEnvelope(res.status, body, parseRetryAfter(res.headers?.get?.("Retry-After") ?? null));
|
|
52
79
|
}
|
|
53
80
|
|
|
54
81
|
// src/v2/keys.ts
|
|
@@ -143,7 +170,7 @@ var OwV2Client = class {
|
|
|
143
170
|
return this.request("GET", `/${type}/${id}/`, { query });
|
|
144
171
|
}
|
|
145
172
|
/**
|
|
146
|
-
* POST /{type}/ -- create. Mints an RFC
|
|
173
|
+
* POST /{type}/ -- create. Mints an RFC 9562 UUIDv7 for element.id when the
|
|
147
174
|
* caller omits one (design ruling D29d) so a retry carrying the same
|
|
148
175
|
* Idempotency-Key is structurally safe. Callers MAY still supply their own id.
|
|
149
176
|
*/
|
|
@@ -289,16 +316,22 @@ function readReplayHeader(headers) {
|
|
|
289
316
|
const v = headers.get("Idempotent-Replay");
|
|
290
317
|
return v != null && v.toLowerCase() === "true";
|
|
291
318
|
}
|
|
292
|
-
function mintUuid() {
|
|
319
|
+
function mintUuid(now = Date.now()) {
|
|
293
320
|
const c = globalThis.crypto;
|
|
294
|
-
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
295
321
|
const bytes = new Uint8Array(16);
|
|
296
322
|
if (c && typeof c.getRandomValues === "function") {
|
|
297
323
|
c.getRandomValues(bytes);
|
|
298
324
|
} else {
|
|
299
325
|
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
300
326
|
}
|
|
301
|
-
|
|
327
|
+
const ms = Math.floor(now);
|
|
328
|
+
bytes[0] = Math.floor(ms / 2 ** 40) & 255;
|
|
329
|
+
bytes[1] = Math.floor(ms / 2 ** 32) & 255;
|
|
330
|
+
bytes[2] = ms >>> 24 & 255;
|
|
331
|
+
bytes[3] = ms >>> 16 & 255;
|
|
332
|
+
bytes[4] = ms >>> 8 & 255;
|
|
333
|
+
bytes[5] = ms & 255;
|
|
334
|
+
bytes[6] = bytes[6] & 15 | 112;
|
|
302
335
|
bytes[8] = bytes[8] & 63 | 128;
|
|
303
336
|
const hex = [];
|
|
304
337
|
for (let i = 0; i < 256; i++) hex.push((i + 256).toString(16).slice(1));
|
|
@@ -347,12 +380,12 @@ var ELEMENT_ICONS = {
|
|
|
347
380
|
creature: "bug_report",
|
|
348
381
|
event: "saved_search",
|
|
349
382
|
family: "supervisor_account",
|
|
350
|
-
institution: "
|
|
383
|
+
institution: "account_balance",
|
|
351
384
|
language: "edit_road",
|
|
352
385
|
law: "gpp_bad",
|
|
353
386
|
location: "castle",
|
|
354
387
|
map: "map",
|
|
355
|
-
marker: "
|
|
388
|
+
marker: "location_on",
|
|
356
389
|
narrative: "menu_book",
|
|
357
390
|
object: "webhook",
|
|
358
391
|
phenomenon: "thunderstorm",
|
|
@@ -1072,7 +1105,9 @@ function familyOf(type) {
|
|
|
1072
1105
|
return ELEMENT_FAMILIES[type];
|
|
1073
1106
|
}
|
|
1074
1107
|
function elementColor(type, mode = "dark") {
|
|
1075
|
-
|
|
1108
|
+
const family = ELEMENT_FAMILIES[type];
|
|
1109
|
+
if (family === void 0) throw new TypeError(`elementColor: unknown element type "${String(type)}"`);
|
|
1110
|
+
return FAMILY_COLORS[family][mode];
|
|
1076
1111
|
}
|
|
1077
1112
|
var FAMILY_ORDER = ["agents", "world", "abstract", "temporal"];
|
|
1078
1113
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlyworlds/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"codegen:check": "python codegen/generate_types.py --check",
|
|
29
29
|
"schema:verify": "python codegen/verify_dist.py",
|
|
30
30
|
"schema:check": "npm run schema:verify && npm run codegen:check",
|
|
31
|
-
"prepublishOnly": "npm run build"
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"release:verify": "node codegen/verify_release.mjs"
|
|
32
33
|
},
|
|
33
34
|
"keywords": [
|
|
34
35
|
"onlyworlds",
|