@onlyworlds/sdk 4.8.0 → 4.8.1

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024-2025 OnlyWorlds
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 OnlyWorlds
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,167 +1,167 @@
1
- # OnlyWorlds TypeScript SDK
2
-
3
- [![npm version](https://badge.fury.io/js/@onlyworlds%2Fsdk.svg)](https://www.npmjs.com/package/@onlyworlds/sdk)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- OnlyWorlds is an open standard for worldbuilding data: 22 element types (characters, locations, events, laws and the rest) that any tool can read and write. This is a typed client for its API, with the schema's constants (element types, icons, colour families, field metadata) generated from the published [schema distribution](https://github.com/OnlyWorlds/schema-dist). It runs in Node 18+ and in the browser through a bundler (ESM only).
7
-
8
- ## Install
9
-
10
- ```bash
11
- npm install @onlyworlds/sdk
12
- ```
13
-
14
- ## First run
15
-
16
- Moppetopia is a public demo world. Its read-only key, `0000000001`, needs no account:
17
-
18
- ```typescript
19
- import { OwV2Client } from '@onlyworlds/sdk';
20
-
21
- const client = new OwV2Client({ apiKey: '0000000001' });
22
- console.log((await client.getWorld()).name);
23
- const { data } = await client.list('character', { filter: { name__icontains: 'fluffington' } });
24
- for (const c of data) console.log(' ', c.name);
25
- ```
26
-
27
- ```
28
- Moppetopia
29
- Admiral Fluffington
30
- ```
31
-
32
- ## Your own world
33
-
34
- Create a world at [onlyworlds.com](https://www.onlyworlds.com); its keys are on the world's page. A new key is shown once, so copy it then.
35
-
36
- - An `ow_r_` key reads, with no PIN. It is the key to give to players or a public site.
37
- - An `ow_w_` key reads and writes. Writes also send a PIN as `apiPin`.
38
- - Anyone who loads a web page can read the keys in its code. Ship only an `ow_r_` key to a browser, and keep write keys and seat secrets on a server.
39
- - For code that writes, give it its own [agent seat](https://onlyworlds.github.io/docs/development/agents), a key for one tool or script. The seat's key and its `ow_s_` secret (sent as `apiPin`) work in one world, and you can remove them without touching your account PIN.
40
-
41
- ```typescript
42
- const writer = new OwV2Client({ apiKey: 'ow_w_…', apiPin: 'ow_s_…' });
43
- ```
44
-
45
- ## Reading and writing
46
-
47
- ```typescript
48
- const location = await writer.create('location', {
49
- name: 'Dragon Peak',
50
- description: 'A mountain peak where dragons nest',
51
- }); // the id is minted client-side when omitted, so a retry creates nothing twice
52
-
53
- const dragon = await writer.create('creature', {
54
- name: 'Vorrath the Ember-Scaled',
55
- location: location.id, // a single link: a UUID or null
56
- });
57
-
58
- const fetched = await writer.get('creature', dragon.id);
59
- // fetched.location === location.id: a field reads the way it writes
60
-
61
- // PATCH replaces the fields it sends; an array is replaced whole
62
- await writer.patch('location', location.id, { supertype: 'Mountain' });
63
-
64
- // For links, the atomic merge; it returns the updated element
65
- const breath = await writer.create('ability', { name: 'Ember Breath' });
66
- await writer.editLinks('creature', dragon.id, 'abilities', { add: [breath.id], remove: [] });
67
-
68
- // Every page of a type
69
- for await (const character of writer.listAll('character')) { /* ... */ }
70
- ```
71
-
72
- A link field has one name in both directions (`location`, not `location_id`). Only `name` is required, and an empty name is stored as `''`. A `world` field in a payload is ignored: the key decides the world, and the client strips it anyway.
73
-
74
- ## Bulk writes
75
-
76
- `/bulk` answers 200 even when some items fail, so check `errors`:
77
-
78
- ```typescript
79
- const res = await writer.bulk(
80
- [
81
- { type: 'character', element: { name: 'A' } },
82
- { type: 'event', element: { name: 'B' } },
83
- ],
84
- { idempotencyKey: crypto.randomUUID() }, // a new key per attempt: a failed batch is cached under its key
85
- );
86
- if (res.errors) {
87
- for (const slot of res.items.filter((s) => s.status >= 400)) {
88
- console.warn(slot.error?.code, slot.error?.message, slot.error?.doc_url);
89
- }
90
- }
91
- ```
92
-
93
- Pass `{ atomic: true }` for all or nothing. After a failed atomic batch nothing was written, but the slots that would have succeeded still read 201: don't record those ids as created. `res.wasReplay` marks an idempotent replay.
94
-
95
- ## Images
96
-
97
- ```typescript
98
- const image = await writer.uploadImage(file); // a Blob, File, ArrayBuffer or Uint8Array
99
- await writer.patch('character', id, { image_url: image.url });
100
- ```
101
-
102
- The SDK asks the API for a single-use ticket, then sends the bytes straight to the upload host, which never sees your key. webp, png, jpeg or avif (no SVG or gif), up to 15 MB. Each upload counts toward the world's daily limit and the account's storage. To upload yourself (for a progress bar), call `createMediaTicket()` and POST the bytes to its `upload_url` with `Authorization: Bearer <ticket>`.
103
-
104
- ```typescript
105
- await writer.removeImage(image.url); // the image_url, or its object key
106
- await writer.patch('character', id, { image_url: null });
107
- ```
108
-
109
- The image's uploader or the world's owner can remove an uploaded image; the space goes back to the uploader's storage. Removal leaves elements as they are, so clear their `image_url` yourself (`createRemovalTicket(key).referenced` says how many still show it). A copy the network already holds can keep answering for a while.
110
-
111
- ## Sync
112
-
113
- ```typescript
114
- let cursor; // opaque and never expires: keep it
115
- for await (const change of client.changesAll(cursor)) {
116
- // changes arrive in order; apply them in order and you converge
117
- }
118
- ```
119
-
120
- Edits to the world itself (its name, calendar, `public_read`) don't appear in the change feed. Read `client.getWorld()` and compare `updated_at`.
121
-
122
- ## Errors
123
-
124
- Every non-2xx response throws `OwApiError` with the API's envelope: `.status`, `.type`, `.code`, `.param` (the field that failed) and `.docUrl`, which is worth showing to your users. A transport failure throws `OwNetworkError`. `err.isValidationError` covers the common case.
125
-
126
- ## Schema constants
127
-
128
- ```typescript
129
- import {
130
- ELEMENT_TYPES, // the 22 type slugs (and the ElementType union)
131
- ELEMENT_ICONS, // a Material Symbols icon per type
132
- ELEMENT_LABELS, // plural display labels
133
- ELEMENT_SECTIONS, // field grouping and display order
134
- FIELD_SCHEMA, // per-field type and link target
135
- elementColor, // colour by family; the icon carries the type
136
- } from '@onlyworlds/sdk';
137
-
138
- elementColor('character', 'dark'); // '#3987e5'
139
- ```
140
-
141
- The four colour families (agents, world, abstract, temporal) are checked for colour-blind separation. Show colour with the icon and label, never alone. [SCHEMA.md](SCHEMA.md) (generated, in the package) describes every field; AI agents should read [AGENTS.md](AGENTS.md) first.
142
-
143
- ## Tokens
144
-
145
- The account's token allowance and ratings, which some OnlyWorlds tools use.
146
-
147
- ```typescript
148
- import { TokenResource } from '@onlyworlds/sdk';
149
- const tokens = new TokenResource(writer);
150
- const status = await tokens.getStatus();
151
- ```
152
-
153
- ## SDK or MCP
154
-
155
- Use this SDK for known operations in your own code (CRUD, sync, bulk). To let an AI assistant that speaks MCP (the Model Context Protocol) explore a world in a chat, connect it to `https://www.onlyworlds.com/mcp` with the same key and PIN as `API-Key` and `API-Pin` headers.
156
-
157
- ## Version 3 and the old API
158
-
159
- 4.x speaks the current (v2) API only. The v1 client (`OnlyWorldsClient`) and CommonJS `require()` stay on 3.x, which remains published and supported: see [the migration guide](docs/migrating-3-to-4.md).
160
-
161
- ## Links
162
-
163
- [Docs](https://onlyworlds.github.io/docs/development/typescript) · [Games](https://onlyworlds.github.io/docs/development/games) · [API reference](https://www.onlyworlds.com/api/docs) · [Issues](https://github.com/OnlyWorlds/sdk/issues) · [Changelog](CHANGELOG.md)
164
-
165
- ## License
166
-
167
- MIT
1
+ # OnlyWorlds TypeScript SDK
2
+
3
+ [![npm version](https://badge.fury.io/js/@onlyworlds%2Fsdk.svg)](https://www.npmjs.com/package/@onlyworlds/sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ OnlyWorlds is an open standard for worldbuilding data: 22 element types (characters, locations, events, laws and the rest) that any tool can read and write. This is a typed client for its API, with the schema's constants (element types, icons, colour families, field metadata) generated from the published [schema distribution](https://github.com/OnlyWorlds/schema-dist). It runs in Node 18+ and in the browser through a bundler (ESM only).
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @onlyworlds/sdk
12
+ ```
13
+
14
+ ## First run
15
+
16
+ Moppetopia is a public demo world. Its read-only key, `0000000001`, needs no account:
17
+
18
+ ```typescript
19
+ import { OwV2Client } from '@onlyworlds/sdk';
20
+
21
+ const client = new OwV2Client({ apiKey: '0000000001' });
22
+ console.log((await client.getWorld()).name);
23
+ const { data } = await client.list('character', { filter: { name__icontains: 'fluffington' } });
24
+ for (const c of data) console.log(' ', c.name);
25
+ ```
26
+
27
+ ```
28
+ Moppetopia
29
+ Admiral Fluffington
30
+ ```
31
+
32
+ ## Your own world
33
+
34
+ Create a world at [onlyworlds.com](https://www.onlyworlds.com); its keys are on the world's page. A new key is shown once, so copy it then.
35
+
36
+ - An `ow_r_` key reads, with no PIN. It is the key to give to players or a public site.
37
+ - An `ow_w_` key reads and writes. Writes also send a PIN as `apiPin`.
38
+ - Anyone who loads a web page can read the keys in its code. Ship only an `ow_r_` key to a browser, and keep write keys and seat secrets on a server.
39
+ - For code that writes, give it its own [agent seat](https://onlyworlds.github.io/docs/development/agents), a key for one tool or script. The seat's key and its `ow_s_` secret (sent as `apiPin`) work in one world, and you can remove them without touching your account PIN.
40
+
41
+ ```typescript
42
+ const writer = new OwV2Client({ apiKey: 'ow_w_…', apiPin: 'ow_s_…' });
43
+ ```
44
+
45
+ ## Reading and writing
46
+
47
+ ```typescript
48
+ const location = await writer.create('location', {
49
+ name: 'Dragon Peak',
50
+ description: 'A mountain peak where dragons nest',
51
+ }); // the id is minted client-side when omitted, so a retry creates nothing twice
52
+
53
+ const dragon = await writer.create('creature', {
54
+ name: 'Vorrath the Ember-Scaled',
55
+ location: location.id, // a single link: a UUID or null
56
+ });
57
+
58
+ const fetched = await writer.get('creature', dragon.id);
59
+ // fetched.location === location.id: a field reads the way it writes
60
+
61
+ // PATCH replaces the fields it sends; an array is replaced whole
62
+ await writer.patch('location', location.id, { supertype: 'Mountain' });
63
+
64
+ // For links, the atomic merge; it returns the updated element
65
+ const breath = await writer.create('ability', { name: 'Ember Breath' });
66
+ await writer.editLinks('creature', dragon.id, 'abilities', { add: [breath.id], remove: [] });
67
+
68
+ // Every page of a type
69
+ for await (const character of writer.listAll('character')) { /* ... */ }
70
+ ```
71
+
72
+ A link field has one name in both directions (`location`, not `location_id`). Only `name` is required, and an empty name is stored as `''`. A `world` field in a payload is ignored: the key decides the world, and the client strips it anyway.
73
+
74
+ ## Bulk writes
75
+
76
+ `/bulk` answers 200 even when some items fail, so check `errors`:
77
+
78
+ ```typescript
79
+ const res = await writer.bulk(
80
+ [
81
+ { type: 'character', element: { name: 'A' } },
82
+ { type: 'event', element: { name: 'B' } },
83
+ ],
84
+ { idempotencyKey: crypto.randomUUID() }, // a new key per attempt: a failed batch is cached under its key
85
+ );
86
+ if (res.errors) {
87
+ for (const slot of res.items.filter((s) => s.status >= 400)) {
88
+ console.warn(slot.error?.code, slot.error?.message, slot.error?.doc_url);
89
+ }
90
+ }
91
+ ```
92
+
93
+ Pass `{ atomic: true }` for all or nothing. After a failed atomic batch nothing was written, but the slots that would have succeeded still read 201: don't record those ids as created. `res.wasReplay` marks an idempotent replay.
94
+
95
+ ## Images
96
+
97
+ ```typescript
98
+ const image = await writer.uploadImage(file); // a Blob, File, ArrayBuffer or Uint8Array
99
+ await writer.patch('character', id, { image_url: image.url });
100
+ ```
101
+
102
+ The SDK asks the API for a single-use ticket, then sends the bytes straight to the upload host, which never sees your key. webp, png, jpeg or avif (no SVG or gif), up to 15 MB. Each upload counts toward the world's daily limit and the account's storage. To upload yourself (for a progress bar), call `createMediaTicket()` and POST the bytes to its `upload_url` with `Authorization: Bearer <ticket>`.
103
+
104
+ ```typescript
105
+ await writer.removeImage(image.url); // the image_url, or its object key
106
+ await writer.patch('character', id, { image_url: null });
107
+ ```
108
+
109
+ The image's uploader or the world's owner can remove an uploaded image; the space goes back to the uploader's storage. Removal leaves elements as they are, so clear their `image_url` yourself (`createRemovalTicket(key).referenced` says how many still show it). A copy the network already holds can keep answering for a while.
110
+
111
+ ## Sync
112
+
113
+ ```typescript
114
+ let cursor; // opaque and never expires: keep it
115
+ for await (const change of client.changesAll(cursor)) {
116
+ // changes arrive in order; apply them in order and you converge
117
+ }
118
+ ```
119
+
120
+ Edits to the world itself (its name, calendar, `public_read`) don't appear in the change feed. Read `client.getWorld()` and compare `updated_at`.
121
+
122
+ ## Errors
123
+
124
+ Every non-2xx response throws `OwApiError` with the API's envelope: `.status`, `.type`, `.code`, `.param` (the field that failed) and `.docUrl`, which is worth showing to your users. A transport failure throws `OwNetworkError`. `err.isValidationError` covers the common case.
125
+
126
+ ## Schema constants
127
+
128
+ ```typescript
129
+ import {
130
+ ELEMENT_TYPES, // the 22 type slugs (and the ElementType union)
131
+ ELEMENT_ICONS, // a Material Symbols icon per type
132
+ ELEMENT_LABELS, // plural display labels
133
+ ELEMENT_SECTIONS, // field grouping and display order
134
+ FIELD_SCHEMA, // per-field type and link target
135
+ elementColor, // colour by family; the icon carries the type
136
+ } from '@onlyworlds/sdk';
137
+
138
+ elementColor('character', 'dark'); // '#3987e5'
139
+ ```
140
+
141
+ The four colour families (agents, world, abstract, temporal) are checked for colour-blind separation. Show colour with the icon and label, never alone. [SCHEMA.md](SCHEMA.md) (generated, in the package) describes every field; AI agents should read [AGENTS.md](AGENTS.md) first.
142
+
143
+ ## Tokens
144
+
145
+ The account's token allowance and ratings, which some OnlyWorlds tools use.
146
+
147
+ ```typescript
148
+ import { TokenResource } from '@onlyworlds/sdk';
149
+ const tokens = new TokenResource(writer);
150
+ const status = await tokens.getStatus();
151
+ ```
152
+
153
+ ## SDK or MCP
154
+
155
+ Use this SDK for known operations in your own code (CRUD, sync, bulk). To let an AI assistant that speaks MCP (the Model Context Protocol) explore a world in a chat, connect it to `https://www.onlyworlds.com/mcp` with the same key and PIN as `API-Key` and `API-Pin` headers.
156
+
157
+ ## Version 3 and the old API
158
+
159
+ 4.x speaks the current (v2) API only. The v1 client (`OnlyWorldsClient`) and CommonJS `require()` stay on 3.x, which remains published and supported: see [the migration guide](docs/migrating-3-to-4.md).
160
+
161
+ ## Links
162
+
163
+ [Docs](https://onlyworlds.github.io/docs/development/typescript) · [Games](https://onlyworlds.github.io/docs/development/games) · [API reference](https://www.onlyworlds.com/api/docs) · [Issues](https://github.com/OnlyWorlds/sdk/issues) · [Changelog](CHANGELOG.md)
164
+
165
+ ## License
166
+
167
+ MIT