@onlyworlds/sdk 4.3.0 → 4.4.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
@@ -44,6 +44,11 @@ against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum)
44
44
  - Under load keel answers 503 `server_busy` with `Retry-After` (`err.isBusy`, `err.retryAfter`
45
45
  in seconds). The client does not retry for you; back off and retry yourself. Works the same
46
46
  in browsers.
47
+ - `created_by` rides every element read: the id of the membership that created it, `null` for
48
+ the world's owner and for anything made before memberships existed. It is read-only; a write
49
+ body that carries it has it dropped, never an error. A **contributor** can change only what
50
+ they created: anything else is 403 `not_author` (`err.isNotAuthor`; retrying will not help).
51
+ `/bulk` reports it per slot, not as a throw.
47
52
  - A string holding an unpaired surrogate (text cut mid-emoji) is a 422 naming the field.
48
53
  Slice strings by code point, not by UTF-16 unit.
49
54
  - Colour carries the element's FAMILY (`elementColor(type, mode)`); the icon
package/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ earlier history lives in git log only.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.4.0] — 2026-10-04 (staged, not published)
9
+
10
+ Membership, as far as the server has shipped it: who created an element, and the refusal a
11
+ contributor meets on someone else's. Additive; nothing is removed or narrows.
12
+
13
+ ### Added
14
+ - **`created_by`** on `OwElementBase` (so on every element type): `string | null`, the id of the
15
+ membership that created the element, `null` for the world's owner and for anything made
16
+ before memberships existed (keel D72 phase 2, live 2026-09-28; keel spec §4). Read-only. The
17
+ schema does not carry it (like `created_at` it is server bookkeeping), so the generator adds
18
+ it to the wire base under the declared mapping. A write body that carries it has it dropped
19
+ by the server, so the client does not strip it and a read body still round-trips.
20
+ - **`OwApiError.isNotAuthor`**: 403 `not_author`, a contributor changing, replacing, relinking
21
+ or deleting an element someone else created. `/bulk` answers 200 and reports it in the item
22
+ slot, so check the slot (documented on `OwBulkItemResult`).
23
+
24
+ ### Fixed
25
+ - **`FIELD_SCHEMA` no longer marks `marker.map/zone/x/y/order` and `pin.map/element_type/element_id/x/y`
26
+ as `required: true`.** The wire requires `name` and nothing else (rulings.yaml `nullable-by-default`,
27
+ Captain 2026-07-28; keel's OpenAPI write schemas say `required: [name]`), but the schema files still
28
+ list those fields and the generator copied the lists, so a form built from the table demanded
29
+ coordinates the server never did. Found by comparing the SDK with keel's OpenAPI document (the
30
+ new `verify-sdk-wire` gate in Assembly). **If you read `required` from this table, marker and pin
31
+ fields now read as optional**, which is what the server accepts. The generator notes the lists it
32
+ ignores on every run and says when canonical stops carrying them.
33
+
34
+ ### Not yet
35
+ - `me()` and `members()` wait for the server routes (keel's M2b). Nothing in this release
36
+ guesses at their names.
37
+
8
38
  ## [4.3.0] — 2026-09-28
9
39
 
10
40
  Errors you can act on: the two 409s told apart, and the busy server's `Retry-After`, in Node and
package/SCHEMA.md CHANGED
@@ -6,7 +6,7 @@ GENERATED from the canonical schema YAML — do not hand-edit (regenerate: `pyth
6
6
  Written for both humans and AI agents reading this package locally.
7
7
 
8
8
  **The shape rules** (v2 wire dialect): every element carries `id` (UUID), `name`, optional
9
- `description`/`supertype`/`subtype`/`image_url`, server-managed `type`/`created_at`/`updated_at`/`change_seq`,
9
+ `description`/`supertype`/`subtype`/`image_url`, server-managed `type`/`created_at`/`updated_at`/`change_seq`/`created_by`,
10
10
  and namespaced extension fields (`x_*` etc.) returned verbatim. Link fields use ONE bare
11
11
  name in both read and write (no `_ids` suffix). Single links are `UUID | null`; multi links
12
12
  are `UUID[]`. **Links are owned one-way**: the type listed below owns the field (e.g.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Every element carries these. The extension index signature admits namespaced
2
2
  * pass-through fields (atlas_* / shadow_* / x_*) returned verbatim by the server.
3
3
  * Derived from base_properties.yaml: `World` is dropped (the API rejects it in
4
- * bodies -- the key determines the world) and the four server-managed fields are
4
+ * bodies -- the key determines the world) and the five server-managed fields are
5
5
  * added, since they ride every wire body and appear in no element YAML. */
6
6
  interface OwElementBase {
7
7
  /** Element type slug (server-managed, read-only). */
@@ -24,6 +24,8 @@ interface OwElementBase {
24
24
  updated_at?: string;
25
25
  /** Per-world change cursor, stamped on every write (server-managed, read-only). */
26
26
  change_seq?: number;
27
+ /** The membership that created this element (server-managed, read-only); null for the world's owner and for anything created before memberships existed. */
28
+ created_by?: string | null;
27
29
  /** Namespaced extension fields (atlas_* / shadow_* / x_*), returned verbatim. */
28
30
  [ext: string]: unknown;
29
31
  }
@@ -1100,27 +1102,22 @@ declare const FIELD_SCHEMA: {
1100
1102
  readonly map: {
1101
1103
  readonly type: "single_link";
1102
1104
  readonly target: "map";
1103
- readonly required: true;
1104
1105
  };
1105
1106
  readonly zone: {
1106
1107
  readonly type: "single_link";
1107
1108
  readonly target: "zone";
1108
- readonly required: true;
1109
1109
  };
1110
1110
  readonly x: {
1111
1111
  readonly type: "integer";
1112
- readonly required: true;
1113
1112
  };
1114
1113
  readonly y: {
1115
1114
  readonly type: "integer";
1116
- readonly required: true;
1117
1115
  };
1118
1116
  readonly z: {
1119
1117
  readonly type: "integer";
1120
1118
  };
1121
1119
  readonly order: {
1122
1120
  readonly type: "integer";
1123
- readonly required: true;
1124
1121
  };
1125
1122
  };
1126
1123
  readonly narrative: {
@@ -1407,24 +1404,19 @@ declare const FIELD_SCHEMA: {
1407
1404
  readonly map: {
1408
1405
  readonly type: "single_link";
1409
1406
  readonly target: "map";
1410
- readonly required: true;
1411
1407
  };
1412
1408
  readonly element_type: {
1413
1409
  readonly type: "text";
1414
- readonly required: true;
1415
1410
  };
1416
1411
  readonly element_id: {
1417
1412
  readonly type: "single_link";
1418
1413
  readonly target: "any";
1419
- readonly required: true;
1420
1414
  };
1421
1415
  readonly x: {
1422
1416
  readonly type: "integer";
1423
- readonly required: true;
1424
1417
  };
1425
1418
  readonly y: {
1426
1419
  readonly type: "integer";
1427
- readonly required: true;
1428
1420
  };
1429
1421
  readonly z: {
1430
1422
  readonly type: "integer";
@@ -1918,6 +1910,8 @@ interface OwBulkItem {
1918
1910
  * One slot of a /bulk response. WIRE-CORRECTED (fixtures P2a/P2c): `status` is
1919
1911
  * the NUMERIC HTTP status of that slot (201/400/...), success slots echo
1920
1912
  * created_at/updated_at, error slots carry an OwErrorBody under `error`.
1913
+ * A contributor's write on someone else's element fails in its slot as
1914
+ * `{status: 403, error: {code: 'not_author'}}` (keel D72), never as a thrown error.
1921
1915
  */
1922
1916
  interface OwBulkItemResult {
1923
1917
  status: number;
@@ -2042,6 +2036,13 @@ declare class OwApiError extends Error {
2042
2036
  get isIdConflict(): boolean;
2043
2037
  /** keel's admission control turned the request away (503 `server_busy`); see retryAfter. */
2044
2038
  get isBusy(): boolean;
2039
+ /**
2040
+ * A contributor tried to change, replace, relink or delete an element someone else
2041
+ * created (403 `not_author`, keel D72 membership phase 2). Retrying will not help; the
2042
+ * element is not theirs. `/bulk` never throws for this: it answers 200 with a per-item
2043
+ * slot `{status: 403, error: {code: 'not_author'}}`. Owners and co-builders never see it.
2044
+ */
2045
+ get isNotAuthor(): boolean;
2045
2046
  }
2046
2047
  /** Network-level failure (fetch rejected) -- no envelope to parse. */
2047
2048
  declare class OwNetworkError extends Error {
package/dist/index.js CHANGED
@@ -39,6 +39,15 @@ var OwApiError = class extends Error {
39
39
  get isBusy() {
40
40
  return this.status === 503 && this.code === "server_busy";
41
41
  }
42
+ /**
43
+ * A contributor tried to change, replace, relink or delete an element someone else
44
+ * created (403 `not_author`, keel D72 membership phase 2). Retrying will not help; the
45
+ * element is not theirs. `/bulk` never throws for this: it answers 200 with a per-item
46
+ * slot `{status: 403, error: {code: 'not_author'}}`. Owners and co-builders never see it.
47
+ */
48
+ get isNotAuthor() {
49
+ return this.status === 403 && this.code === "not_author";
50
+ }
42
51
  };
43
52
  var OwNetworkError = class extends Error {
44
53
  constructor(message, cause) {
@@ -846,12 +855,12 @@ var FIELD_SCHEMA = {
846
855
  subtype: { type: "text", required: false },
847
856
  image_url: { type: "text", required: false },
848
857
  // Details
849
- map: { type: "single_link", target: "map", required: true },
850
- zone: { type: "single_link", target: "zone", required: true },
851
- x: { type: "integer", required: true },
852
- y: { type: "integer", required: true },
858
+ map: { type: "single_link", target: "map" },
859
+ zone: { type: "single_link", target: "zone" },
860
+ x: { type: "integer" },
861
+ y: { type: "integer" },
853
862
  z: { type: "integer" },
854
- order: { type: "integer", required: true }
863
+ order: { type: "integer" }
855
864
  },
856
865
  narrative: {
857
866
  // Base fields (shared by all elements)
@@ -944,11 +953,11 @@ var FIELD_SCHEMA = {
944
953
  subtype: { type: "text", required: false },
945
954
  image_url: { type: "text", required: false },
946
955
  // Details
947
- map: { type: "single_link", target: "map", required: true },
948
- element_type: { type: "text", required: true },
949
- element_id: { type: "single_link", target: "any", required: true },
950
- x: { type: "integer", required: true },
951
- y: { type: "integer", required: true },
956
+ map: { type: "single_link", target: "map" },
957
+ element_type: { type: "text" },
958
+ element_id: { type: "single_link", target: "any" },
959
+ x: { type: "integer" },
960
+ y: { type: "integer" },
952
961
  z: { type: "integer" }
953
962
  },
954
963
  relation: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlyworlds/sdk",
3
- "version": "4.3.0",
3
+ "version": "4.4.0",
4
4
  "description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
5
5
  "type": "module",
6
6
  "exports": {