@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 CHANGED
@@ -1,34 +1,54 @@
1
- # For AI agents using @onlyworlds/sdk
2
-
3
- **Start with [SCHEMA.md](SCHEMA.md)** (in this package): the full generated schema reference —
4
- every type, every field with its meaning, link directions, families, icons, display sections.
5
- It is generated from the same canonical YAML as the types, so it cannot drift.
6
-
7
- **What this package is**: the canonical typed TypeScript client for the OnlyWorlds v2 API,
8
- plus the canonical constants (element types, icons, colour families, field schema).
9
- OnlyWorlds is an open standard for portable world data — 22 element types, UUID-linked.
10
-
11
- **Use the v2 surface.** `OwV2Client` + the `V2ElementType` slug union + the generated
12
- interfaces in `types.generated.ts` (emitted from the canonical schema YAML, validated
13
- against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum) is frozen
14
- legacy — do not build new work on it.
15
-
16
- **SDK vs MCP server — pick correctly**:
17
- - Known, deterministic operations (CRUD, sync, bulk) → **this SDK**. Typed calls, typed
18
- responses, far cheaper than tool-schema reasoning.
19
- - Live exploration of a user's world from a chat/agent context → the **MCP server** at
20
- `https://www.onlyworlds.com/mcp` (same `API-Key`/`API-Pin` headers, 11 tools).
21
-
22
- **Wire facts that bite** (full details in README):
23
- - Never send a `"world"` field in payloads — world identity comes from the API key (422 otherwise).
24
- - v2 link fields use ONE name both directions (no `_ids` suffix — that is v1 dialect only).
25
- - PATCH is destructive on sent fields; use `editLinks` (atomic add/remove) for relationships.
26
- - World-meta changes do NOT appear in `/changes` — poll `GET /world` separately.
27
- - Extension fields: `x_<toolname>_*` is the sanctioned namespace for tool-specific state;
28
- unknown unprefixed fields 422.
29
- - Colour carries the element's FAMILY (`elementColor(type, mode)`); the icon
30
- (`ELEMENT_ICONS`) carries the TYPE. Icon + label are required alongside colour, not optional.
31
-
32
- **Auth**: prefixed keys — `ow_w_` (read+write), `ow_r_` (read-only, no PIN — the share
33
- primitive), `ow_a_` (account Bearer). Demo keys `0000000000`–`0000000009` are read-only
34
- test credentials against real data.
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: business
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: place
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
- * Blessed Django-style filters: __icontains, __in, __gte, __lte, __isnull,
1962
- * supertype/subtype equality. Unknown params 422 loudly server-side -- the
1963
- * client passes them through and lets the platform name the typo.
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
- constructor(status: number, code: string | null, message: string, docUrl: string | null, detail: unknown, type?: string | null, param?: string | null);
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
- /** Same Idempotency-Key replayed with a different payload. */
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-4122 UUID for element.id when the
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
- /** Same Idempotency-Key replayed with a different payload. */
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-4122 UUID for element.id when the
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
- bytes[6] = bytes[6] & 15 | 64;
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: "business",
383
+ institution: "account_balance",
351
384
  language: "edit_road",
352
385
  law: "gpp_bad",
353
386
  location: "castle",
354
387
  map: "map",
355
- marker: "place",
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
- return FAMILY_COLORS[ELEMENT_FAMILIES[type]][mode];
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.1.0",
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",