@onlyworlds/sdk 4.63.0 → 4.65.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/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ earlier history lives in git log only.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.65.0] - 2026-10-23
9
+
10
+ ### Fixed
11
+
12
+ - **`patchWorld` takes a `getWorld()` body back again** (keel #571, live 2026-10-23). keel's `GET /world` now serves `origin`, and a PATCH naming it is a 422 (an unknown key), so a world body read and sent back failed. `patchWorld` now leaves out `origin`, and `id` too: keel answers `id` with the same 422 (it is not a key PATCH takes), so the round trip 4.57.1's entry promised never held for a whole body. `owner_character` is still sent.
13
+
14
+ ### Added
15
+
16
+ - **`OwWorldMeta.origin?: OwWorldOrigin`**, read-only: where the world first came from, `{ source, producer }`, both keys always present, each the folder format's provenance line (spec 4.2) as the tool that made the world from a folder gave it, or `null`. Types `OwWorldOrigin`, `OwWorldSource` (`server`, `world`, `at`) and `OwWorldProducer` (`tool`, `version`, `input`), each open to keys keel keeps as given. The origin is written as the plain keys `source` and `producer`: `patchWorld({ source, producer })` sends them as given (an object, or `null` to clear).
17
+ - **The World's extension keys on `OwWorldMeta`**: `x_*`, `atlas_*` and `shadow_*`, typed `unknown` by template-literal index signatures (the first is `x_names`, the names profile `typeWord` reads). `patchWorld` sends every one as given, `null` included (keel removes the key); over keel's 64 KB cap on the whole store the answer is a 422 with `param: extensions`.
18
+ - Tests: a full `GET /world` body sent back sends neither `origin` nor `id` and keeps `x_names`; `source`, `producer` and a key of each extension prefix pass untouched, `null` included; the published types declare `origin` and its two keys, and the documented calls (`patchWorld(await getWorld())`, `patchWorld({ source, producer, x_names })`, `typeWord(world, …)`) compile against the built `.d.ts`. Watched failing on mutants: `origin` and `id` each out of the strip list, `origin` out of `OwWorldMeta`, and a narrowed `patchWorld` parameter.
19
+
20
+ ## [4.64.0] - 2026-10-23
21
+
22
+ ### Changed
23
+
24
+ - **Every options object in the package now refuses an unknown key**, not only the client's typed methods (4.51.0 to 4.53.0): `readWorldFile`, `parseDate`, `resolveCalendarLens`, `lookupWorld`, `renderElement`, `planPhoto`, `preparePhoto`, the client's `request()` escape hatch, `listAll` (with its own keys: a `cursor` it was handed was overwritten unread), the deprecated `tokens.consume` and `new OwSignInError`'s third argument throw a `TypeError` naming the function, the key and the keys it takes, before anything is read, decoded, parsed or sent. A dropped key was never harmless: `readWorldFile(bytes, { maxbytes: 1e6 })` read with the 2 GiB budget, `parseDate(cfg, input, { Now: 12 })` parsed as if the world had no now, and W11's `list('character', { name__icontains })` read every Character and a cleanup script deleted 99 of them (2026-10-22). A plain-JavaScript caller passing a misspelt key will now see a `TypeError` where the call used to run with the default; TypeScript callers were already refused at compile time. `writeWorldFile` and `typeWord` keep their refusals on the same shared check; `typeWord`'s message now reads like the others (`typeWord: unknown option 'plural'. The keys are supertype, many.`).
25
+ - Tests: one test calls every options parameter of the exported surface with `{ __unknown__: 1 }` and asserts a `TypeError` naming the key, nothing sent; a second reads `src/index.ts` with the TypeScript checker and fails on any exported object parameter that is neither in that list nor named as data (a calendar config, a world index, an element), so a new options object cannot ship unfenced. Watched failing on mutants of the `readWorldFile`, `parseDate`, `preparePhoto` and `request()` fences, and on an exported options function added without one.
26
+
8
27
  ## [4.63.0] - 2026-10-23
9
28
 
10
29
  ### Added
package/README.md CHANGED
@@ -184,6 +184,8 @@ To poll for news cheaply, `client.changesHead()` returns `{ head, cursor, epoch,
184
184
 
185
185
  Edits to the world itself (its name, calendar, `public_read`) don't appear in the change feed. Read `client.getWorld()` and compare `updated_at`.
186
186
 
187
+ `client.patchWorld(partial)` writes the world's own settings, and takes a `getWorld()` body back: the keys keel does not take (`id`, `origin`, `slug`, `public_page` and the owner's switches) are left out. The world's `origin` is written as the plain keys `source` and `producer` (each an object or `null`), and extension keys (`x_*`, `atlas_*`, `shadow_*`) are sent as given; `null` removes one.
188
+
187
189
  ### Only if unchanged
188
190
 
189
191
  A client that edits offline, or from a copy it has held a while, can ask for an update to land only if nobody changed the element since it was read: pass the `change_seq` you read as `ifMatch`, on `patch`, `editLinks` or `delete` (since 4.61.0: a delete of an element someone changed since you read it is refused, nothing deleted).
package/dist/index.d.ts CHANGED
@@ -1968,9 +1968,47 @@ interface OwWorldMeta {
1968
1968
  * the owner on the world page; read-only on the wire, and patchWorld strips it.
1969
1969
  */
1970
1970
  agent_writes_proposed?: boolean;
1971
+ /**
1972
+ * Where the world first came from (keel #571, #477): the folder format's provenance lines (spec
1973
+ * 4.2) as the tool that made the world from a folder gave them, both keys always present, each
1974
+ * null when not known. Read-only on the wire, and patchWorld strips it: write it as the plain
1975
+ * keys `source` and `producer` (`patchWorld({ source, producer })`, each an object or null).
1976
+ */
1977
+ origin?: OwWorldOrigin;
1971
1978
  created_at?: string | null;
1972
1979
  /** Moves when the world's own fields change, not on element writes. */
1973
1980
  updated_at?: string | null;
1981
+ /**
1982
+ * Extension keys (keel #571): a tool's own setting on the world, any JSON value, served at the
1983
+ * top level as an element's are (the first is `x_names`, the names profile: see `typeWord`).
1984
+ * patchWorld sends them as given; `null` removes one; 64 KB in all, else a 422 `param: extensions`.
1985
+ */
1986
+ [extension: `x_${string}`]: unknown;
1987
+ [extension: `atlas_${string}`]: unknown;
1988
+ [extension: `shadow_${string}`]: unknown;
1989
+ [field: string]: unknown;
1990
+ }
1991
+ /**
1992
+ * OwWorldMeta.origin (keel #571, #477), as keel's `World` component types it: both keys always
1993
+ * present, each an object or null. Kept as the writer gave it; keel checks only that `source.world`
1994
+ * and `producer.tool` are strings when present.
1995
+ */
1996
+ interface OwWorldOrigin {
1997
+ source: OwWorldSource | null;
1998
+ producer: OwWorldProducer | null;
1999
+ }
2000
+ /** The folder format's `source` line (spec 4.2): the server and world a folder was exported from, and when. Kept as given. */
2001
+ interface OwWorldSource {
2002
+ server?: string;
2003
+ world?: string;
2004
+ at?: string;
2005
+ [field: string]: unknown;
2006
+ }
2007
+ /** The folder format's `producer` line (spec 4.2): the tool that made a folder, its version, and what it made it from. Kept as given. */
2008
+ interface OwWorldProducer {
2009
+ tool?: string;
2010
+ version?: string;
2011
+ input?: string;
1974
2012
  [field: string]: unknown;
1975
2013
  }
1976
2014
  /** OwWorldMeta.public_page (keel #471), as keel's `World` component types it: all six keys, always. */
@@ -3421,7 +3459,10 @@ declare class OwV2Client {
3421
3459
  /**
3422
3460
  * PATCH /world -- partial world-meta update. The element write's server-managed keys are
3423
3461
  * stripped, and so are the world's own read-only ones (WORLD_READ_ONLY_FIELDS: `slug` and
3424
- * `public_page`, keel #471; `agent_writes_proposed`, keel #519; set on the world page, not here).
3462
+ * `public_page`, keel #471; `agent_writes_proposed`, keel #519; set on the world page, not here;
3463
+ * `origin`, keel #571; `id`), so a `getWorld()` body can be sent back. The world's origin is
3464
+ * written as `source` and `producer` (keel #571, each an object or null, served under `origin`);
3465
+ * extension keys (`x_*`, `atlas_*`, `shadow_*`) are sent as given, and `null` removes one.
3425
3466
  */
3426
3467
  patchWorld(partial: Record<string, unknown>): Promise<OwWorldMeta>;
3427
3468
  /**
@@ -4371,7 +4412,8 @@ interface OwReadWorldFileOptions {
4371
4412
  declare function simpleCaseFold(text: string): string;
4372
4413
  /**
4373
4414
  * Read a world file. Throws `OwWorldFileError` saying why, having returned
4374
- * nothing; the caller writes only what this returns.
4415
+ * nothing; the caller writes only what this returns. An unknown option is a
4416
+ * `TypeError` before the bytes are looked at.
4375
4417
  */
4376
4418
  declare function readWorldFile(bytes: Uint8Array, opts?: OwReadWorldFileOptions): OwReadWorldFile;
4377
4419
  interface OwWriteWorldFileOptions {
@@ -4535,13 +4577,13 @@ declare function buildWorldIndex<T extends OwLookupElement>(elements: Iterable<T
4535
4577
  * elements whose other text holds the question's words. An empty question
4536
4578
  * returns no hits.
4537
4579
  */
4538
- declare function lookupWorld<T extends OwLookupElement>(index: OwWorldIndex<T>, question: string, { limit }?: OwLookupOptions): OwLookupHit<T>[];
4580
+ declare function lookupWorld<T extends OwLookupElement>(index: OwWorldIndex<T>, question: string, opts?: OwLookupOptions): OwLookupHit<T>[];
4539
4581
  /**
4540
4582
  * A compact text card for one element, for a person or a model: name, type,
4541
4583
  * kind, description, its fields, its links by name (or how many point outside
4542
4584
  * the data), and what links to it.
4543
4585
  */
4544
- declare function renderElement<T extends OwLookupElement>(element: T, index: OwWorldIndex<T>, { descMax, linkMax, incomingMax }?: OwRenderOptions): string;
4586
+ declare function renderElement<T extends OwLookupElement>(element: T, index: OwWorldIndex<T>, opts?: OwRenderOptions): string;
4545
4587
 
4546
4588
  /**
4547
4589
  * Context for keel's AI proxy (POST /api/v2/account/ask, Keel #159): the elements a
@@ -4641,9 +4683,9 @@ declare function planPhoto(source: {
4641
4683
  type: string;
4642
4684
  }, opts?: OwPhotoOptions): OwPhotoPlan;
4643
4685
  /**
4644
- * Shrink and convert a picture for upload. Throws `TypeError` outside a
4645
- * browser, for a file the browser cannot decode, or when the result is still
4646
- * over `maxBytes`.
4686
+ * Shrink and convert a picture for upload. Throws `TypeError` for an unknown
4687
+ * option (before anything is decoded), outside a browser, for a file the
4688
+ * browser cannot decode, or when the result is still over `maxBytes`.
4647
4689
  */
4648
4690
  declare function preparePhoto(input: Blob, opts?: OwPhotoOptions): Promise<OwPreparedPhoto>;
4649
4691
 
@@ -4908,7 +4950,8 @@ declare function worldCalendars(elements: Iterable<OwCalendarElement>): WorldCal
4908
4950
  *
4909
4951
  * `fallbackToOnly`: when no calendar was ever chosen (`activeId` is `undefined`, not
4910
4952
  * `null`) and the world holds exactly one, use it. `null` is an explicit "no calendar" and
4911
- * stays null. With several calendars and no choice, nothing is guessed.
4953
+ * stays null. With several calendars and no choice, nothing is guessed. An unknown option is a
4954
+ * TypeError (the caller's bug, not the world's data).
4912
4955
  */
4913
4956
  declare function resolveCalendarLens(elements: Iterable<OwCalendarElement>, activeId: string | null | undefined, opts?: {
4914
4957
  fallbackToOnly?: boolean;
@@ -5218,4 +5261,4 @@ declare class TokenResource {
5218
5261
  getEncryptionInfo(): Promise<EncryptionInfo>;
5219
5262
  }
5220
5263
 
5221
- 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_ACRONYMS, FIELD_SCHEMA, FILE_EXTENSIONS, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAccountWorld, 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 OwDeleteReason, type OwDraught, type OwDraughtComponent, type OwDraughtItem, type OwDraughtPolicy, type OwElement, type OwElementBase, type OwErrorBody, type OwFileAddedBy, type OwFileDownload, type OwFileKind, type OwFileRow, type OwFileTicket, type OwFrontDoor, type OwHiddenRow, 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, type OwMintedKey, type OwNamesMap, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, type OwPlayerJoined, OwPreconditionFailedError, type OwPreparedPhoto, type OwProposal, type OwProposalAccepted, type OwProposalConflict, type OwProposalList, type OwProposalQuery, type OwProposalState, type OwProposeInput, type OwProposed, OwProposedError, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwSeat, type OwSeatCreated, type OwSignInBegin, type OwSignInBegun, type OwSignInComplete, OwSignInError, type OwSignInReason, type OwSignInResult, type OwSignInStorage, type OwSignedIn, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, OwStaleProposalError, type OwUploadedFile, type OwUploadedImage, OwV2Client, type OwWordPair, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldFileRow, type OwWorldFiles, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWorldProposalQuery, type OwWorldPublicPage, type OwWriteDoor, type OwWriteWorldFileOptions, PHOTO_MAX_BYTES, PHOTO_TYPES, PUBLIC_FIELDS, type ParseError, type ParseOptions, type ParseResult, ROOM_PAIRS, 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, WORLD_FILE_MEDIA_TYPE, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, base64url, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, fieldLabel, fileKindOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isKnownOf, isLeapYear, isParseError, isRoomElement, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, pkceChallenge, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, simpleCaseFold, typeWord, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
5264
+ 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_ACRONYMS, FIELD_SCHEMA, FILE_EXTENSIONS, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, type OwAccountWorld, 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 OwDeleteReason, type OwDraught, type OwDraughtComponent, type OwDraughtItem, type OwDraughtPolicy, type OwElement, type OwElementBase, type OwErrorBody, type OwFileAddedBy, type OwFileDownload, type OwFileKind, type OwFileRow, type OwFileTicket, type OwFrontDoor, type OwHiddenRow, 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, type OwMintedKey, type OwNamesMap, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, type OwPlayerJoined, OwPreconditionFailedError, type OwPreparedPhoto, type OwProposal, type OwProposalAccepted, type OwProposalConflict, type OwProposalList, type OwProposalQuery, type OwProposalState, type OwProposeInput, type OwProposed, OwProposedError, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwSeat, type OwSeatCreated, type OwSignInBegin, type OwSignInBegun, type OwSignInComplete, OwSignInError, type OwSignInReason, type OwSignInResult, type OwSignInStorage, type OwSignedIn, type OwSortAddToRow, type OwSortAnswer, type OwSortAskRow, type OwSortLinkRow, type OwSortMakeRow, type OwSortNote, type OwSortRef, type OwSortRow, OwStaleProposalError, type OwUploadedFile, type OwUploadedImage, OwV2Client, type OwWordPair, type OwWorldFileEntry, OwWorldFileError, type OwWorldFileReason, type OwWorldFileRow, type OwWorldFiles, type OwWorldIndex, type OwWorldLink, type OwWorldMeta, type OwWorldOrigin, type OwWorldProducer, type OwWorldProposalQuery, type OwWorldPublicPage, type OwWorldSource, type OwWriteDoor, type OwWriteWorldFileOptions, PHOTO_MAX_BYTES, PHOTO_TYPES, PUBLIC_FIELDS, type ParseError, type ParseOptions, type ParseResult, ROOM_PAIRS, 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, WORLD_FILE_MEDIA_TYPE, type WorldCalendar, absYearForEraYear, absoluteYear, approxYearLength, base64url, breakdownStep, buildAskContext, buildWorldIndex, detectKeyKind, displayYear, elementColor, eraForStep, errorFromResponse, familyOf, fieldLabel, fileKindOf, floorDiv, floorMod, formatDuration, formatMidLabel, formatStep, formatYearLabel, getElementIcon, getElementLabel, isAgentDoor, isDemoKey, isKnownOf, isLeapYear, isParseError, isRoomElement, kindCanWrite, lookupWorld, monthStartSteps, parseDate, parseErrorEnvelope, pinExpectation, pkceChallenge, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, simpleCaseFold, typeWord, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
package/dist/index.js CHANGED
@@ -56,6 +56,19 @@ function keyedId(idempotencyKey, index) {
56
56
  ${index}`);
57
57
  }
58
58
 
59
+ // src/options.ts
60
+ function unknownKey(opts, keys) {
61
+ if (!opts || typeof opts !== "object") return void 0;
62
+ for (const k of Object.keys(opts)) if (!keys.includes(k)) return k;
63
+ return void 0;
64
+ }
65
+ function refuseUnknownOptions(fn, opts, keys, after = "") {
66
+ const k = unknownKey(opts, keys);
67
+ if (k !== void 0) {
68
+ throw new TypeError(`${fn}: unknown option '${k}'. The keys are ${keys.join(", ")}.${after ? ` ${after}` : ""}`);
69
+ }
70
+ }
71
+
59
72
  // src/v2/errors.ts
60
73
  var OwApiError = class extends Error {
61
74
  constructor(status, code, message, docUrl, detail, type = null, param = null, retryAfter = null) {
@@ -342,6 +355,7 @@ var OwNetworkError = class extends Error {
342
355
  };
343
356
  var OwSignInError = class extends Error {
344
357
  constructor(reason, message, opts = {}) {
358
+ refuseUnknownOptions("OwSignInError", opts, ["status", "code", "retryAfter"]);
345
359
  super(message);
346
360
  this.name = "OwSignInError";
347
361
  this.reason = reason;
@@ -603,7 +617,10 @@ var OwV2Client = class {
603
617
  /**
604
618
  * PATCH /world -- partial world-meta update. The element write's server-managed keys are
605
619
  * stripped, and so are the world's own read-only ones (WORLD_READ_ONLY_FIELDS: `slug` and
606
- * `public_page`, keel #471; `agent_writes_proposed`, keel #519; set on the world page, not here).
620
+ * `public_page`, keel #471; `agent_writes_proposed`, keel #519; set on the world page, not here;
621
+ * `origin`, keel #571; `id`), so a `getWorld()` body can be sent back. The world's origin is
622
+ * written as `source` and `producer` (keel #571, each an object or null, served under `origin`);
623
+ * extension keys (`x_*`, `atlas_*`, `shadow_*`) are sent as given, and `null` removes one.
607
624
  */
608
625
  async patchWorld(partial) {
609
626
  const body = sanitizePayload(partial);
@@ -652,7 +669,7 @@ var OwV2Client = class {
652
669
  // -- Element CRUD --------------------------------------------------------
653
670
  /** GET /{type}/ -- one cursor page. */
654
671
  async list(type, params = {}) {
655
- only("list", params, LIST_PARAM_KEYS, "A wire filter (name, name__icontains, supertype, subtype, characters) goes under filter: { ... }.");
672
+ only("list", params, LIST_PARAM_KEYS, LIST_FILTER_HINT);
656
673
  const query = buildQuery({
657
674
  limit: params.limit ?? this.pageSize,
658
675
  cursor: params.cursor,
@@ -691,6 +708,7 @@ var OwV2Client = class {
691
708
  }
692
709
  /** Cursor-walk every page of a type. Politeness: uses config pageSize. */
693
710
  async *listAll(type, params = {}) {
711
+ only("listAll", params, LIST_ALL_PARAM_KEYS, LIST_FILTER_HINT);
694
712
  let cursor;
695
713
  do {
696
714
  const page = await this.list(type, { ...params, cursor });
@@ -1718,6 +1736,7 @@ var OwV2Client = class {
1718
1736
  * sanitizePayload — callers own their body shape.
1719
1737
  */
1720
1738
  async request(method, path, opts = {}) {
1739
+ only("request", opts, REQUEST_OPTION_KEYS);
1721
1740
  if (opts.ifMatch !== void 0 && !(Number.isInteger(opts.ifMatch) && opts.ifMatch >= 0)) {
1722
1741
  throw new TypeError(`ifMatch must be a whole number (an element's change_seq), got ${String(opts.ifMatch)}`);
1723
1742
  }
@@ -1784,7 +1803,7 @@ var HeadPollState = class {
1784
1803
  }
1785
1804
  };
1786
1805
  var READ_ONLY_FIELDS = ["world", "type", "created_at", "updated_at", "change_seq", "known_of"];
1787
- var WORLD_READ_ONLY_FIELDS = ["slug", "public_page", "public_read", "agent_narrator_rule", "agent_writes_proposed"];
1806
+ var WORLD_READ_ONLY_FIELDS = ["id", "slug", "public_page", "public_read", "agent_narrator_rule", "agent_writes_proposed", "origin"];
1788
1807
  function proposalsPath(type, id) {
1789
1808
  return `/${type}/${encodeURIComponent(id)}/proposals`;
1790
1809
  }
@@ -1930,12 +1949,26 @@ function objectKey(keyOrUrl) {
1930
1949
  return decodeURIComponent(new URL(keyOrUrl).pathname.replace(/^\/+/, ""));
1931
1950
  }
1932
1951
  var LIST_PARAM_KEYS = ["limit", "cursor", "ids", "createdBy", "expand", "fields", "filter", "viewAs"];
1952
+ var LIST_ALL_PARAM_KEYS = LIST_PARAM_KEYS.filter((k) => k !== "cursor");
1953
+ var LIST_FILTER_HINT = "A wire filter (name, name__icontains, supertype, subtype, characters) goes under filter: { ... }.";
1954
+ var REQUEST_OPTION_KEYS = [
1955
+ "query",
1956
+ "body",
1957
+ "idempotencyKey",
1958
+ "auth",
1959
+ "allowEmpty",
1960
+ "replayAware",
1961
+ "write",
1962
+ "elementWrite",
1963
+ "proposable",
1964
+ "ifMatch",
1965
+ "ifNoneMatch",
1966
+ "onEtag"
1967
+ ];
1933
1968
  function only(method, obj, keys, hint = "") {
1934
- if (!obj || typeof obj !== "object") return;
1935
- for (const k of Object.keys(obj)) {
1936
- if (!keys.includes(k)) {
1937
- throw new TypeError(`${method}: unknown parameter '${k}'. ${hint ? hint + " " : ""}The keys are ${keys.join(", ")}. Nothing was sent.`);
1938
- }
1969
+ const k = unknownKey(obj, keys);
1970
+ if (k !== void 0) {
1971
+ throw new TypeError(`${method}: unknown parameter '${k}'. ${hint ? hint + " " : ""}The keys are ${keys.join(", ")}. Nothing was sent.`);
1939
1972
  }
1940
1973
  }
1941
1974
  var CLIENT_CONFIG_KEYS = ["apiKey", "apiPin", "baseUrl", "pageSize", "changesPageSize", "fetch", "agent", "client"];
@@ -2884,6 +2917,7 @@ var OwWorldFileError = class extends Error {
2884
2917
  this.reason = reason;
2885
2918
  }
2886
2919
  };
2920
+ var READ_OPTION_KEYS = ["maxBytes", "maxEntries", "skip"];
2887
2921
  var refuse = (reason, message) => {
2888
2922
  throw new OwWorldFileError(reason, message);
2889
2923
  };
@@ -3163,6 +3197,7 @@ function checkFileAndFolder(files) {
3163
3197
  }
3164
3198
  }
3165
3199
  function readWorldFile(bytes, opts = {}) {
3200
+ refuseUnknownOptions("readWorldFile", opts, READ_OPTION_KEYS, "Nothing was read.");
3166
3201
  const maxBytes = opts.maxBytes ?? WORLD_FILE_MAX_BYTES;
3167
3202
  const maxEntries = opts.maxEntries ?? WORLD_FILE_MAX_ENTRIES;
3168
3203
  const entries = centralDirectory(bytes);
@@ -3254,11 +3289,7 @@ var RECORDS_MANIFEST = "records/RECORDS.json";
3254
3289
  function asksPrivate(opts) {
3255
3290
  if (opts === void 0 || opts === null) return false;
3256
3291
  if (typeof opts !== "object") throw new TypeError(`writeWorldFile: options must be an object, such as { includePrivateRecords: true }. Nothing was written.`);
3257
- for (const k of Object.keys(opts)) {
3258
- if (!WRITE_OPTION_KEYS.includes(k)) {
3259
- throw new TypeError(`writeWorldFile: unknown option '${k}'. The keys are ${WRITE_OPTION_KEYS.join(", ")}. Nothing was written.`);
3260
- }
3261
- }
3292
+ refuseUnknownOptions("writeWorldFile", opts, WRITE_OPTION_KEYS, "Nothing was written.");
3262
3293
  const asked = opts.includePrivateRecords;
3263
3294
  if (asked !== void 0 && typeof asked !== "boolean") {
3264
3295
  throw new TypeError(`writeWorldFile: includePrivateRecords must be true or false; got ${JSON.stringify(asked) ?? String(asked)}. Nothing was written.`);
@@ -3369,7 +3400,7 @@ function isRoomElement(el) {
3369
3400
 
3370
3401
  // src/names.ts
3371
3402
  var SLUGS = new Set(ELEMENT_TYPES);
3372
- var OPTION_KEYS = /* @__PURE__ */ new Set(["supertype", "many"]);
3403
+ var OPTION_KEYS = ["supertype", "many"];
3373
3404
  var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
3374
3405
  function word(pair, many) {
3375
3406
  if (!isObject(pair) || typeof pair.one !== "string") return null;
@@ -3380,9 +3411,7 @@ function word(pair, many) {
3380
3411
  return plural === "" ? one : plural;
3381
3412
  }
3382
3413
  function typeWord(world, type, opts = {}) {
3383
- for (const key of Object.keys(opts ?? {})) {
3384
- if (!OPTION_KEYS.has(key)) throw new TypeError(`typeWord: unknown option "${key}" (known: supertype, many)`);
3385
- }
3414
+ refuseUnknownOptions("typeWord", opts, OPTION_KEYS);
3386
3415
  const map = world?.x_names;
3387
3416
  if (!isObject(map) || map.version !== 1) return null;
3388
3417
  const { types, supertypes } = map;
@@ -3405,6 +3434,8 @@ function typeWord(world, type, opts = {}) {
3405
3434
  }
3406
3435
 
3407
3436
  // src/lookup.ts
3437
+ var LOOKUP_OPTION_KEYS = ["limit"];
3438
+ var RENDER_OPTION_KEYS = ["descMax", "linkMax", "incomingMax"];
3408
3439
  var STOP = /* @__PURE__ */ new Set([
3409
3440
  "a",
3410
3441
  "an",
@@ -3708,7 +3739,9 @@ function similarWords(w, vocab) {
3708
3739
  }
3709
3740
  return res;
3710
3741
  }
3711
- function lookupWorld(index, question, { limit = 8 } = {}) {
3742
+ function lookupWorld(index, question, opts = {}) {
3743
+ refuseUnknownOptions("lookupWorld", opts, LOOKUP_OPTION_KEYS);
3744
+ const { limit = 8 } = opts;
3712
3745
  const seeds = 12;
3713
3746
  const q = words(normalize(question));
3714
3747
  const qSet = new Set(q);
@@ -3825,7 +3858,9 @@ function lookupWorld(index, question, { limit = 8 } = {}) {
3825
3858
  for (const [id, c] of cand) if (SPATIAL.has(index.byId.get(id).type)) c.score *= 0.85;
3826
3859
  return [...cand.entries()].map(([id, c]) => ({ element: index.byId.get(id), score: Math.round(c.score * 10) / 10, reasons: c.reasons })).sort((a, b) => b.score - a.score || String(a.element.name).length - String(b.element.name).length || (a.element.id < b.element.id ? -1 : 1)).slice(0, limit);
3827
3860
  }
3828
- function renderElement(element, index, { descMax = 500, linkMax = 12, incomingMax = 10 } = {}) {
3861
+ function renderElement(element, index, opts = {}) {
3862
+ refuseUnknownOptions("renderElement", opts, RENDER_OPTION_KEYS);
3863
+ const { descMax = 500, linkMax = 12, incomingMax = 10 } = opts;
3829
3864
  const el = element;
3830
3865
  const name = (id) => index.byId.get(id)?.name ?? "?";
3831
3866
  const kind = [el.supertype, el.subtype].filter((x) => x && x !== "undefined").join(" / ");
@@ -3909,7 +3944,9 @@ function buildAskContext(items, index) {
3909
3944
  // src/photo.ts
3910
3945
  var PHOTO_TYPES = ["image/webp", "image/png", "image/jpeg", "image/avif"];
3911
3946
  var PHOTO_MAX_BYTES = 15 * 1024 * 1024;
3947
+ var PHOTO_OPTION_KEYS = ["maxSide", "quality", "maxBytes"];
3912
3948
  function planPhoto(source, opts = {}) {
3949
+ refuseUnknownOptions("planPhoto", opts, PHOTO_OPTION_KEYS);
3913
3950
  const maxSide = opts.maxSide ?? 2048;
3914
3951
  if (!(maxSide > 0)) throw new TypeError("preparePhoto: maxSide must be a positive number");
3915
3952
  if (!(source.width > 0 && source.height > 0)) throw new TypeError("preparePhoto: the picture has no size");
@@ -3934,6 +3971,7 @@ function makeCanvas(width, height) {
3934
3971
  throw new TypeError("preparePhoto needs a browser: no canvas here");
3935
3972
  }
3936
3973
  async function preparePhoto(input, opts = {}) {
3974
+ refuseUnknownOptions("preparePhoto", opts, PHOTO_OPTION_KEYS);
3937
3975
  const quality = opts.quality ?? 0.82;
3938
3976
  const maxBytes = opts.maxBytes ?? PHOTO_MAX_BYTES;
3939
3977
  const decode = globalThis.createImageBitmap;
@@ -4272,6 +4310,7 @@ function formatMidLabel(cfg, step) {
4272
4310
  if (b.monthName != null && b.day != null) return `${b.day} ${b.monthName} ${b.year}`;
4273
4311
  return formatYearLabel(cfg, step);
4274
4312
  }
4313
+ var PARSE_OPTION_KEYS = ["now"];
4275
4314
  function absYearForEraYear(cfg, era, Y) {
4276
4315
  if (era.restart === false) return Y;
4277
4316
  const base = era.year_base ?? 1;
@@ -4290,6 +4329,7 @@ function defaultEra(cfg, now) {
4290
4329
  return [...pool].sort((a, b) => b.start - a.start)[0];
4291
4330
  }
4292
4331
  function parseDate(cfg, input, opts = {}) {
4332
+ refuseUnknownOptions("parseDate", opts, PARSE_OPTION_KEYS);
4293
4333
  const raw = input.trim();
4294
4334
  if (raw === "") return { error: "empty date" };
4295
4335
  if (/^-?\d+$/.test(raw)) return { step: parseInt(raw, 10) };
@@ -4420,6 +4460,7 @@ function worldCalendars(elements) {
4420
4460
  return out;
4421
4461
  }
4422
4462
  function resolveCalendarLens(elements, activeId, opts) {
4463
+ refuseUnknownOptions("resolveCalendarLens", opts, ["fallbackToOnly"]);
4423
4464
  if (activeId === void 0 && opts?.fallbackToOnly) {
4424
4465
  const all = worldCalendars(elements);
4425
4466
  return all.length === 1 ? all[0] : null;
@@ -4499,6 +4540,7 @@ var TokenResource = class {
4499
4540
  * ```
4500
4541
  */
4501
4542
  async consume(params) {
4543
+ refuseUnknownOptions("consume", params, ["amount", "service", "sessionId", "metadata"], "Nothing was sent.");
4502
4544
  return this.req("POST", "/tokens/consume/", {
4503
4545
  body: {
4504
4546
  amount: params.amount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlyworlds/sdk",
3
- "version": "4.63.0",
3
+ "version": "4.65.0",
4
4
  "description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
5
5
  "type": "module",
6
6
  "exports": {