@onlyworlds/sdk 4.29.0 → 4.30.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/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ earlier history lives in git log only.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.30.1] — 2026-10-16
9
+
10
+ ### Fixed
11
+ - **`changes()` and `changesAll()` hand back every cursor as a string**, as `OwChangesPage.cursor` has
12
+ always said. keel sends the cursor of a page at the head of the feed (an empty page, for any key but
13
+ a guest's) as a number, and it passed through as one: a walk of an up-to-date world returned
14
+ `{ cursor: 818 }`, so a cursor stored and compared as a string could miss. `changesHead()` already
15
+ did this. Found on production with a demo key.
16
+
17
+ ## [4.30.0] — 2026-10-16
18
+
19
+ ### Added
20
+ - **A friend with only a link** (keel #353, D111):
21
+ - `redeemJoin(code, { name?, agentName? })`: redeems a join code that needs no account, at `POST /join`,
22
+ sending no credential whatever key the client holds. The answer's `kind` tells the two apart:
23
+ `OwPlayerJoined` (a read key, `ow_r_`, no PIN, and the Character the player reads as) or
24
+ `OwAgentJoined` (a seat's write key, secret and `.env` lines). Shown once; the code is spent, so a
25
+ lost answer means a new link. Every bad code is one 404.
26
+ - `createWorldLink(worldId, { reader: 'player', character? })`: a player link, guest only; `character`
27
+ is a Character's id or `new` (the default). `OwWorldLink.character` names it on the list.
28
+ - `player` joins the `kind` of `OwMe` and `OwMember`.
29
+ - **A client with no key**: `apiKey` is optional. `new OwV2Client()` (or an empty key) has `keyKind: 'none'`
30
+ and can call `health`, `previewJoin` and `redeemJoin`, which is what a friend with only a link has; any
31
+ other call is a TypeError before anything is sent. Until now a key was required even for the routes
32
+ that send none.
33
+
8
34
  ## [4.29.0] — 2026-10-16
9
35
 
10
36
  ### Added
package/dist/index.d.ts CHANGED
@@ -2171,7 +2171,8 @@ interface OwMe {
2171
2171
  name: string;
2172
2172
  };
2173
2173
  scope: 'read' | 'write';
2174
- kind: 'owner' | 'person' | 'agent';
2174
+ /** `player`: a friend with no account, reading as a guest (keel #353). */
2175
+ kind: 'owner' | 'person' | 'agent' | 'player';
2175
2176
  role: 'owner' | OwMemberRole;
2176
2177
  /** The membership id (what `created_by` holds for what this key creates), or null for the owner. */
2177
2178
  membership: string | null;
@@ -2185,7 +2186,7 @@ interface OwMe {
2185
2186
  interface OwMember {
2186
2187
  /** The membership id (what `created_by` holds), or null for the owner. */
2187
2188
  id: string | null;
2188
- kind: 'owner' | 'person' | 'agent';
2189
+ kind: 'owner' | 'person' | 'agent' | 'player';
2189
2190
  role: 'owner' | OwMemberRole;
2190
2191
  /** The Character that is this member, or null. */
2191
2192
  character: string | null;
@@ -2209,8 +2210,8 @@ interface OwAllowance {
2209
2210
  }
2210
2211
  /** POST /join/preview: what a join code holds, read without using it (keel #212). */
2211
2212
  interface OwJoinPreview {
2212
- /** `person`: an invite link, accepted with acceptJoin. `agent`: redeemed by an AI agent at POST /join. */
2213
- reader: 'person' | 'agent' | (string & {});
2213
+ /** `person`: an invite link, accepted with acceptJoin. `agent`: redeemed by an AI agent at POST /join. `player`: a friend with no account redeems it with redeemJoin for a read key (keel #353). */
2214
+ reader: 'person' | 'agent' | 'player' | (string & {});
2214
2215
  world: {
2215
2216
  name: string;
2216
2217
  description: string | null;
@@ -2230,14 +2231,14 @@ interface OwJoinPreview {
2230
2231
  interface OwWorldLink {
2231
2232
  /** The link's id: revokeWorldLink takes it. */
2232
2233
  id: string;
2233
- /** `person`: accepted signed in, with acceptJoin. `agent`: redeemed by an AI agent at POST /join. */
2234
- reader: 'person' | 'agent';
2234
+ /** `person`: accepted signed in, with acceptJoin. `agent`: redeemed by an AI agent at POST /join. `player`: redeemed with no account at POST /join, for a read key. */
2235
+ reader: 'person' | 'agent' | 'player';
2235
2236
  role: OwMemberRole;
2236
2237
  /** The name the owner gave an agent link on the website, or empty. */
2237
2238
  label: string;
2238
2239
  /** The name the link is signed with, or empty (the invite line then reads "Someone"). */
2239
2240
  inviter_name: string;
2240
- /** Whether redeeming gives the agent a Character of its own. Always false on a person link. */
2241
+ /** Whether redeeming gives the agent a Character of its own. Always false on a person link, always true on a player link. */
2241
2242
  with_character: boolean;
2242
2243
  /** The code's last four characters, to tell links apart. */
2243
2244
  last4: string;
@@ -2248,6 +2249,11 @@ interface OwWorldLink {
2248
2249
  /** 0 or 1: a link works once. */
2249
2250
  uses: number;
2250
2251
  status: 'pending' | 'used' | 'revoked' | 'expired';
2252
+ /** A player link only: the Character the player reads as; null while a link for a fresh Character is unused. `name` is null if that Character was deleted (the redeem then makes a fresh one). */
2253
+ character?: {
2254
+ id: string;
2255
+ name: string | null;
2256
+ } | null;
2251
2257
  [k: string]: unknown;
2252
2258
  }
2253
2259
  /** POST /account/worlds/{id}/links: the new link, with its code. The code is in this answer only: keel keeps a hash. */
@@ -2259,6 +2265,58 @@ interface OwCreatedWorldLink extends OwWorldLink {
2259
2265
  /** The one line to send: who invites, to which world, and the link. */
2260
2266
  line: string;
2261
2267
  }
2268
+ /** POST /join, an agent's code: its seat, write key and secret, shown once (keel #212; `kind` since #353). */
2269
+ interface OwAgentJoined {
2270
+ kind: 'agent';
2271
+ world: {
2272
+ id: string;
2273
+ name: string;
2274
+ description: string;
2275
+ [k: string]: unknown;
2276
+ };
2277
+ seat: {
2278
+ id: string;
2279
+ role: OwMemberRole;
2280
+ agent_name: string;
2281
+ [k: string]: unknown;
2282
+ };
2283
+ with_character: boolean;
2284
+ character: {
2285
+ id: string;
2286
+ name: string;
2287
+ } | null;
2288
+ /** The seat's write key (`API-Key`). */
2289
+ key: string;
2290
+ /** The seat's secret, sent as `API-Pin`. */
2291
+ pin: string;
2292
+ base_url: string;
2293
+ /** The lines of a `.env` file for the agent. */
2294
+ env: string;
2295
+ next: string[];
2296
+ members: Array<Record<string, unknown>>;
2297
+ [k: string]: unknown;
2298
+ }
2299
+ /** POST /join, a player's code: a read key for a friend with no account, reading as a guest (keel #353). Shown once. */
2300
+ interface OwPlayerJoined {
2301
+ kind: 'player';
2302
+ /** A read key (`ow_r_`): no PIN, a player reads only. */
2303
+ key: string;
2304
+ world: {
2305
+ id: string;
2306
+ name: string;
2307
+ description: string;
2308
+ [k: string]: unknown;
2309
+ };
2310
+ /** The Character the player reads as. */
2311
+ character: {
2312
+ id: string;
2313
+ name: string;
2314
+ };
2315
+ role: 'guest';
2316
+ [k: string]: unknown;
2317
+ }
2318
+ /** POST /join's answer: tell the two apart by `kind`. */
2319
+ type OwJoined = OwAgentJoined | OwPlayerJoined;
2262
2320
  /** One live agent seat, as GET /account/worlds/{id}/seats lists it (keel #355, D106). No key or secret. */
2263
2321
  interface OwSeat {
2264
2322
  /** The seat's membership id: removeSeat takes it, and GET /members names the seat by it. */
@@ -2357,8 +2415,12 @@ interface ListParams {
2357
2415
  filter?: Record<string, string | number | boolean>;
2358
2416
  }
2359
2417
  interface OwClientConfig {
2360
- /** The key: an ow_w_ (write) or ow_r_ (read) world key, an ow_a_ account token (sent as a Bearer token, for the account routes), or a 10-digit legacy key. */
2361
- apiKey: string;
2418
+ /**
2419
+ * The key: an ow_w_ (write) or ow_r_ (read) world key, an ow_a_ account token (sent as a Bearer token, for the account routes), or a 10-digit legacy key.
2420
+ * Leave it out for a client that holds none, such as a friend with only a link (since 4.30.0): it can call health,
2421
+ * previewJoin and redeemJoin, and any other call is a TypeError before anything is sent.
2422
+ */
2423
+ apiKey?: string;
2362
2424
  /**
2363
2425
  * The PIN, needed for writes when the world has one, and for legacy-key reads of private worlds.
2364
2426
  * Prefixed keys read without a PIN. A string, not a number: '0123' !== 123.
@@ -2533,7 +2595,9 @@ type OwKeyKind =
2533
2595
  /** ow_a_ -- account Bearer token for /account/* routes; can mint world keys. */
2534
2596
  | 'account'
2535
2597
  /** Grandfathered 10-digit key. Needs PIN to read private worlds. */
2536
- | 'legacy' | 'unknown';
2598
+ | 'legacy' | 'unknown'
2599
+ /** No key: a client for the routes that send none (health, previewJoin, redeemJoin), as a friend with only a link has. */
2600
+ | 'none';
2537
2601
  /** Demo range 0000000000-0000000009: read-only aliases, safe as live read gates. */
2538
2602
  declare function isDemoKey(key: string): boolean;
2539
2603
  declare function detectKeyKind(key: string): OwKeyKind;
@@ -2568,7 +2632,7 @@ declare class OwV2Client {
2568
2632
  private readonly apiPin;
2569
2633
  private readonly fetchImpl;
2570
2634
  private readonly agent;
2571
- constructor(config: OwClientConfig);
2635
+ constructor(config?: OwClientConfig);
2572
2636
  /** GET /health -- unauthenticated liveness pulse. */
2573
2637
  health(): Promise<unknown>;
2574
2638
  /**
@@ -2671,6 +2735,8 @@ declare class OwV2Client {
2671
2735
  * view changed (a guest's, or its role): drop the cursor, walk from zero, and
2672
2736
  * replace the local copy rather than merging into it.
2673
2737
  * Citizenship: heaviest route on the platform; default page size is polite.
2738
+ * keel sends the cursor of a page at the head as a number (an empty page, for anyone
2739
+ * but a guest); it is handed back as a string, like every cursor here.
2674
2740
  */
2675
2741
  changes(opts?: {
2676
2742
  since?: string;
@@ -2769,6 +2835,21 @@ declare class OwV2Client {
2769
2835
  * 404 `not_found` (`err.isNotFound`). `code` is the part of the link after `#`.
2770
2836
  */
2771
2837
  previewJoin(code: string): Promise<OwJoinPreview>;
2838
+ /**
2839
+ * POST /join -- redeem a join code that needs no account: a player's (a friend reading as
2840
+ * a guest, keel #353) or an AI agent's. Sends no credential, whatever key this client holds.
2841
+ * 201 with `kind` telling the two answers apart: `player` gives a read key (`ow_r_`, no PIN)
2842
+ * and the Character it reads as; `agent` gives a seat's write key, secret and `.env` lines.
2843
+ * Both are shown once, and the code is spent: a lost answer cannot be replayed (the owner
2844
+ * sees the row never used, and makes a new link). `name` (a player's, optional: the
2845
+ * Character's name or "Player" when left out) and `agentName` (an agent's, required for an
2846
+ * agent code). Every bad code is the same 404 `not_found`; a person's code is redeemed with
2847
+ * acceptJoin instead.
2848
+ */
2849
+ redeemJoin(code: string, opts?: {
2850
+ name?: string;
2851
+ agentName?: string;
2852
+ }): Promise<OwJoined>;
2772
2853
  /**
2773
2854
  * POST /account/join -- accept an invite link's code as the account this client's
2774
2855
  * `ow_a_` token belongs to: 201 with the new membership. Refusals: 404 `not_found`
@@ -2781,17 +2862,21 @@ declare class OwV2Client {
2781
2862
  /**
2782
2863
  * POST /account/worlds/{worldId}/links -- a new invite link for a world this client's
2783
2864
  * `ow_a_` token owns (keel C1): `reader` is required (`person`, accepted with acceptJoin;
2784
- * `agent`, redeemed at POST /join); `role` defaults to contributor, `days` (1, 7 or 30)
2785
- * to 7; `withCharacter` (agent links only) to true. 201 with the code, shown in this
2865
+ * `agent`, redeemed at POST /join; `player`, a friend with no account, redeemed with
2866
+ * redeemJoin for a read key, keel #353); `role` defaults to contributor (a player link is
2867
+ * guest, and refuses any other), `days` (1, 7 or 30) to 7; `withCharacter` (agent links
2868
+ * only) to true; `character` (player links only) a Character's id or `new` (the default):
2869
+ * whoever opens the link first reads as it, until removed. 201 with the code, shown in this
2786
2870
  * answer only. Owner only: anyone else, and a world that doesn't exist, gets 404
2787
2871
  * `not_found` (`err.isNotFound`). A world key is a TypeError before anything is sent.
2788
2872
  */
2789
2873
  createWorldLink(worldId: string, opts: {
2790
- reader: 'person' | 'agent';
2874
+ reader: 'person' | 'agent' | 'player';
2791
2875
  role?: OwMemberRole;
2792
2876
  days?: 1 | 7 | 30;
2793
2877
  inviterName?: string;
2794
2878
  withCharacter?: boolean;
2879
+ character?: string;
2795
2880
  }): Promise<OwCreatedWorldLink>;
2796
2881
  /**
2797
2882
  * GET /account/worlds/{worldId}/links -- the world's invite links, without their codes:
@@ -3842,4 +3927,4 @@ declare class TokenResource {
3842
3927
  getEncryptionInfo(): Promise<EncryptionInfo>;
3843
3928
  }
3844
3929
 
3845
- export { AGENT_DOORS, ASK_LIMITS, type AccessKeyResponse, CALENDAR_FIELD, CALENDAR_SUPERTYPE, type CalendarConfig, type CalendarEra, type CalendarIntercalary, type CalendarMonths, type CalendarWeek, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, type EraDomain, FAMILY_COLORS, FAMILY_ORDER, FIELD_SCHEMA, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAllowance, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreatedWorldLink, type OwElement, type OwElementBase, type OwErrorBody, type OwJoinPreview, type OwJoinedMembership, type OwKeyKind, type OwLinkEdit, type OwLookupElement, type OwLookupHit, type OwLookupLink, type OwLookupOptions, type OwLookupReason, type OwMe, type OwMediaTicket, type OwMember, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwSeat, type OwSeatCreated, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, type OwUploadedImage, OwV2Client, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWriteDoor, PHOTO_MAX_BYTES, PHOTO_TYPES, type ParseError, type ParseOptions, type ParseResult, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type StepBreakdown, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, WORLD_FILE_MAX_BYTES, WORLD_FILE_MAX_ENTRIES, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isLeapYear, isParseError, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
3930
+ export { AGENT_DOORS, ASK_LIMITS, type AccessKeyResponse, CALENDAR_FIELD, CALENDAR_SUPERTYPE, type CalendarConfig, type CalendarEra, type CalendarIntercalary, type CalendarMonths, type CalendarWeek, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, type EraDomain, FAMILY_COLORS, FAMILY_ORDER, FIELD_SCHEMA, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAgentJoined, type OwAllowance, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreatedWorldLink, type OwElement, type OwElementBase, type OwErrorBody, type OwJoinPreview, type OwJoined, type OwJoinedMembership, type OwKeyKind, type OwLinkEdit, type OwLookupElement, type OwLookupHit, type OwLookupLink, type OwLookupOptions, type OwLookupReason, type OwMe, type OwMediaTicket, type OwMember, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, type OwPlayerJoined, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwSeat, type OwSeatCreated, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, type OwUploadedImage, OwV2Client, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWriteDoor, PHOTO_MAX_BYTES, PHOTO_TYPES, type ParseError, type ParseOptions, type ParseResult, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type StepBreakdown, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, WORLD_FILE_MAX_BYTES, WORLD_FILE_MAX_ENTRIES, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isLeapYear, isParseError, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
package/dist/index.js CHANGED
@@ -249,11 +249,10 @@ var DEFAULT_BASE_URL = "https://www.onlyworlds.com/api/v2";
249
249
  var DEFAULT_PAGE_SIZE = 100;
250
250
  var DEFAULT_CHANGES_PAGE_SIZE = 100;
251
251
  var OwV2Client = class {
252
- constructor(config) {
253
- if (!config.apiKey) throw new Error("OwV2Client: apiKey is required");
254
- this.apiKey = config.apiKey;
252
+ constructor(config = {}) {
253
+ this.apiKey = config.apiKey ?? "";
255
254
  this.apiPin = config.apiPin || void 0;
256
- this.keyKind = detectKeyKind(config.apiKey);
255
+ this.keyKind = config.apiKey ? detectKeyKind(config.apiKey) : "none";
257
256
  this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
258
257
  this.pageSize = config.pageSize ?? DEFAULT_PAGE_SIZE;
259
258
  this.changesPageSize = config.changesPageSize ?? DEFAULT_CHANGES_PAGE_SIZE;
@@ -432,10 +431,13 @@ var OwV2Client = class {
432
431
  * view changed (a guest's, or its role): drop the cursor, walk from zero, and
433
432
  * replace the local copy rather than merging into it.
434
433
  * Citizenship: heaviest route on the platform; default page size is polite.
434
+ * keel sends the cursor of a page at the head as a number (an empty page, for anyone
435
+ * but a guest); it is handed back as a string, like every cursor here.
435
436
  */
436
437
  async changes(opts = {}) {
437
438
  const query = buildQuery({ since: opts.since, limit: opts.limit ?? this.changesPageSize });
438
- return this.request("GET", "/changes/", { query });
439
+ const page = await this.request("GET", "/changes/", { query });
440
+ return { ...page, cursor: String(page.cursor) };
439
441
  }
440
442
  /**
441
443
  * GET /changes?head=true -- where the feed is now, without a page: no element is read.
@@ -569,6 +571,23 @@ var OwV2Client = class {
569
571
  async previewJoin(code) {
570
572
  return this.request("POST", "/join/preview", { auth: false, body: { code } });
571
573
  }
574
+ /**
575
+ * POST /join -- redeem a join code that needs no account: a player's (a friend reading as
576
+ * a guest, keel #353) or an AI agent's. Sends no credential, whatever key this client holds.
577
+ * 201 with `kind` telling the two answers apart: `player` gives a read key (`ow_r_`, no PIN)
578
+ * and the Character it reads as; `agent` gives a seat's write key, secret and `.env` lines.
579
+ * Both are shown once, and the code is spent: a lost answer cannot be replayed (the owner
580
+ * sees the row never used, and makes a new link). `name` (a player's, optional: the
581
+ * Character's name or "Player" when left out) and `agentName` (an agent's, required for an
582
+ * agent code). Every bad code is the same 404 `not_found`; a person's code is redeemed with
583
+ * acceptJoin instead.
584
+ */
585
+ async redeemJoin(code, opts = {}) {
586
+ const body = { code };
587
+ if (opts.name !== void 0) body.name = opts.name;
588
+ if (opts.agentName !== void 0) body.agent_name = opts.agentName;
589
+ return this.request("POST", "/join", { auth: false, body });
590
+ }
572
591
  /**
573
592
  * POST /account/join -- accept an invite link's code as the account this client's
574
593
  * `ow_a_` token belongs to: 201 with the new membership. Refusals: 404 `not_found`
@@ -586,8 +605,11 @@ var OwV2Client = class {
586
605
  /**
587
606
  * POST /account/worlds/{worldId}/links -- a new invite link for a world this client's
588
607
  * `ow_a_` token owns (keel C1): `reader` is required (`person`, accepted with acceptJoin;
589
- * `agent`, redeemed at POST /join); `role` defaults to contributor, `days` (1, 7 or 30)
590
- * to 7; `withCharacter` (agent links only) to true. 201 with the code, shown in this
608
+ * `agent`, redeemed at POST /join; `player`, a friend with no account, redeemed with
609
+ * redeemJoin for a read key, keel #353); `role` defaults to contributor (a player link is
610
+ * guest, and refuses any other), `days` (1, 7 or 30) to 7; `withCharacter` (agent links
611
+ * only) to true; `character` (player links only) a Character's id or `new` (the default):
612
+ * whoever opens the link first reads as it, until removed. 201 with the code, shown in this
591
613
  * answer only. Owner only: anyone else, and a world that doesn't exist, gets 404
592
614
  * `not_found` (`err.isNotFound`). A world key is a TypeError before anything is sent.
593
615
  */
@@ -600,6 +622,7 @@ var OwV2Client = class {
600
622
  if (opts.days !== void 0) body.days = opts.days;
601
623
  if (opts.inviterName !== void 0) body.inviter_name = opts.inviterName;
602
624
  if (opts.withCharacter !== void 0) body.with_character = opts.withCharacter;
625
+ if (opts.character !== void 0) body.character = opts.character;
603
626
  return this.request("POST", `/account/worlds/${encodeURIComponent(worldId)}/links`, { body });
604
627
  }
605
628
  /**
@@ -743,6 +766,9 @@ var OwV2Client = class {
743
766
  if (opts.ifMatch !== void 0 && !(Number.isInteger(opts.ifMatch) && opts.ifMatch >= 0)) {
744
767
  throw new TypeError(`ifMatch must be a whole number (an element's change_seq), got ${String(opts.ifMatch)}`);
745
768
  }
769
+ if (opts.auth !== false && this.keyKind === "none") {
770
+ throw new TypeError(`${method} ${path} needs a key, and this client holds none: without one, only health, previewJoin and redeemJoin work`);
771
+ }
746
772
  const url = `${this.baseUrl}${path}${opts.query ? `?${opts.query}` : ""}`;
747
773
  const headers = {};
748
774
  if (opts.auth !== false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlyworlds/sdk",
3
- "version": "4.29.0",
3
+ "version": "4.30.1",
4
4
  "description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
5
5
  "type": "module",
6
6
  "exports": {