@onlyworlds/sdk 4.75.0 → 4.76.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/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # For AI agents using @onlyworlds/sdk
2
2
 
3
- **Current as of**: SDK **4.x** · schema-dist **v0.33.0-dist.66** (canonical 00.33.00).
3
+ **Current as of**: SDK **4.x** · schema-dist **v0.33.0-dist.69** (canonical 00.33.00).
4
4
  This line is asserted by `codegen:check` in CI — if the pin moves and this file is not
5
5
  re-read against it, the check fails rather than letting this document rot quietly.
6
6
 
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ earlier history lives in git log only.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [4.76.1] - 2026-10-25
9
+
10
+ ### Changed
11
+
12
+ - **Schema repinned to schema-dist v0.33.0-dist.69** (from dist.68; same canonical 00.33.00). The calendar fixture's `v2-except-chain-gregorian` case gains 12 steps, the days around Leap Day in 1900, 2000 and 2100 (no Leap Day in 1900 and 2100, one in 2000), each checked against LegendKeeper's own reading; a reader whose days ignore `except` while its year count keeps it misreads only these. The test replays all 64 cases, now 198 steps. The script that generates the amendment's cases (`scripts/calendar-fixture-cases.mjs`) adds those century days and keeps the case's earlier sampled steps as published; its 14 cases equal the published ones. No generated type changed.
13
+
14
+ ## [4.76.0] - 2026-10-25
15
+
16
+ ### Added
17
+
18
+ - **Calendar version 2: a leap rule with exceptions and an offset** (the calendar profile's amendment, schema-dist v0.33.0-dist.68). An intercalary entry with `every_years` may carry `except`, divisors that flip the rule in order (`every_years: 4, except: [100, 400]`: every 4, not every 100, yet every 400), and `offset`, added to the year index before the rule (1 for a calendar with no year 0). Gregorian is `every_years: 4, except: [100, 400], offset: 1`. A value that uses either key is `version: 2`; `readCalendarConfig` reads versions 1 and 2, reads the two keys only under version 2, and refuses an `except` that is not strictly ascending with each entry a multiple of the one before (the first of `every_years`), or an `offset` that is not a whole number. `validateCalendarConfig` names each of these, and the keys under version 1. Year arithmetic stays closed-form. `intercalaryInYear(cfg, entry, year)` says whether an intercalary day falls in a year.
19
+
20
+ ### Changed
21
+
22
+ - **An era with no `end` runs to the next up era's start**, skipping down eras, whatever the list order. An up era listed before a down era with the same pivot no longer ends where it begins: with eras `After` (up, from 0) and `Before` (down, pivot 0), step 5 reads `6 AF`, where it read `5` with no era.
23
+ - **Schema repinned to schema-dist v0.33.0-dist.68** (from dist.66; same canonical 00.33.00). The calendar fixture's 64 cases are replayed by a test, which no test did before; the import map profile (serial 67) is vendored with the distribution and not read by this package. No generated type changed.
24
+
8
25
  ## [4.75.0] - 2026-10-25
9
26
 
10
27
  ### Changed
package/SCHEMA.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OnlyWorlds Schema Reference
2
2
 
3
- **Source**: https://github.com/OnlyWorlds/schema-dist @ **v0.33.0-dist.66** — canonical schema **00.33.00**, published 2026-10-25.
3
+ **Source**: https://github.com/OnlyWorlds/schema-dist @ **v0.33.0-dist.69** — canonical schema **00.33.00**, published 2026-10-25.
4
4
 
5
5
  GENERATED from the canonical schema YAML — do not hand-edit (regenerate: `python codegen/generate_types.py`).
6
6
  Written for both humans and AI agents reading this package locally.
package/dist/index.d.ts CHANGED
@@ -5030,8 +5030,13 @@ interface CalendarIntercalary {
5030
5030
  name: string;
5031
5031
  /** 0 = before month 1; k = after month k. Requires months. */
5032
5032
  after_month: number;
5033
- /** Present only in years where floorMod(absYear, N) === 0 (the leap rule). */
5033
+ /** Present only in years where floorMod(absYear + offset, N) === 0 (the leap rule). */
5034
5034
  every_years?: number;
5035
+ /** Version 2: divisors that flip the rule in order, each a multiple of the one before
5036
+ * (`every_years: 4, except: [100, 400]`: every 4, not every 100, yet every 400). */
5037
+ except?: number[];
5038
+ /** Version 2: the rule reads year absYear + offset (default 0; 1 for a calendar with no year 0). */
5039
+ offset?: number;
5035
5040
  /** Counts in the free-running week (default true). The Shire lock = opting
5036
5041
  * exactly Mid-year's Day + Overlithe out (364 = 52 × 7). */
5037
5042
  in_week?: boolean;
@@ -5089,8 +5094,10 @@ declare function validateCalendarConfig(cfg: CalendarConfig): string[];
5089
5094
  * back to bare numbers, never a broken axis.
5090
5095
  */
5091
5096
  declare function readCalendarConfig(value: unknown): CalendarConfig | null;
5092
- /** True when absolute year y contains the leap entry with period N. */
5097
+ /** True when absolute year y contains a leap entry with period N (the version-1 rule). */
5093
5098
  declare function isLeapYear(y: number, N: number): boolean;
5099
+ /** True when the intercalary day falls in absolute year y: always without `every_years`, else by its rule. */
5100
+ declare function intercalaryInYear(cfg: CalendarConfig, ic: CalendarIntercalary, y: number): boolean;
5094
5101
  /** First step of absolute year y. yearStart(0) === 0 by construction. */
5095
5102
  declare function yearStartStep(cfg: CalendarConfig, y: number): number;
5096
5103
  /** Length in steps of absolute year y. */
@@ -5549,4 +5556,4 @@ declare class TokenResource {
5549
5556
  getEncryptionInfo(): Promise<EncryptionInfo>;
5550
5557
  }
5551
5558
 
5552
- 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 OwAskContextOptions, type OwAskLeftOut, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreateAccountWorldInput, type OwCreatedAccountWorld, type OwCreatedWorldLink, type OwDeleteReason, type OwDraught, type OwDraughtCompact, 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 OwProposedNewElement, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwReviewInput, type OwReviewed, 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 OwWorldProposalList, type OwWorldProposalQuery, type OwWorldProposalRow, type OwWorldPublicPage, type OwWorldSource, type OwWriteDoor, type OwWriteWorldFileOptions, type OwWriteWorldFileReport, PHOTO_MAX_BYTES, PHOTO_TYPES, PUBLIC_FIELDS, type ParseError, type ParseOptions, type ParseResult, REVIEW_PROPOSED_LABEL, 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, nfc, parseDate, parseErrorEnvelope, pinExpectation, pkceChallenge, planPhoto, preparePhoto, readCalendarConfig, readCalendarValue, readWorldFile, renderElement, resolveCalendarLens, resolveEraDomains, simpleCaseFold, typeWord, validateCalendarConfig, weekdayForStep, worldCalendars, writeWorldFile, yearLength, yearStartStep };
5559
+ 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 OwAskContextOptions, type OwAskLeftOut, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangeDelete, type OwChangeUpsert, type OwChangesPage, type OwClientConfig, type OwCreateAccountWorldInput, type OwCreatedAccountWorld, type OwCreatedWorldLink, type OwDeleteReason, type OwDraught, type OwDraughtCompact, 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 OwProposedNewElement, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, type OwResyncReason, type OwReviewInput, type OwReviewed, 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 OwWorldProposalList, type OwWorldProposalQuery, type OwWorldProposalRow, type OwWorldPublicPage, type OwWorldSource, type OwWriteDoor, type OwWriteWorldFileOptions, type OwWriteWorldFileReport, PHOTO_MAX_BYTES, PHOTO_TYPES, PUBLIC_FIELDS, type ParseError, type ParseOptions, type ParseResult, REVIEW_PROPOSED_LABEL, 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, intercalaryInYear, isAgentDoor, isDemoKey, isKnownOf, isLeapYear, isParseError, isRoomElement, kindCanWrite, lookupWorld, monthStartSteps, nfc, 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
@@ -4287,7 +4287,7 @@ function floorMod(a, b) {
4287
4287
  }
4288
4288
  function validateCalendarConfig(cfg) {
4289
4289
  const errors = [];
4290
- if (cfg.version !== 1) errors.push(`Unsupported calendar version: ${cfg.version}`);
4290
+ if (cfg.version !== 1 && cfg.version !== 2) errors.push(`Unsupported calendar version: ${cfg.version}`);
4291
4291
  if (!cfg.grain || typeof cfg.grain !== "string") errors.push("Grain is required");
4292
4292
  const months = cfg.bundles?.months;
4293
4293
  if (months) {
@@ -4317,6 +4317,11 @@ function validateCalendarConfig(cfg) {
4317
4317
  if (ic.every_years != null && (!Number.isInteger(ic.every_years) || ic.every_years < 1)) {
4318
4318
  errors.push(`Festival day "${ic.name}": every N yrs must be a whole number, 1 or more`);
4319
4319
  }
4320
+ const rule = leapRuleProblem(ic);
4321
+ if (rule) errors.push(`Festival day "${ic.name}": ${rule}`);
4322
+ if (cfg.version === 1 && (ic.except != null || ic.offset != null)) {
4323
+ errors.push(`Festival day "${ic.name}": except and offset need calendar version 2`);
4324
+ }
4320
4325
  }
4321
4326
  const eras = cfg.eras ?? [];
4322
4327
  for (const era of eras) {
@@ -4345,7 +4350,7 @@ function validateCalendarConfig(cfg) {
4345
4350
  function readCalendarConfig(value) {
4346
4351
  if (typeof value !== "object" || value === null) return null;
4347
4352
  const cfg = value;
4348
- if (cfg.version !== 1 || typeof cfg.grain !== "string") return null;
4353
+ if (cfg.version !== 1 && cfg.version !== 2 || typeof cfg.grain !== "string") return null;
4349
4354
  const months = cfg.bundles?.months;
4350
4355
  if (months && (!Array.isArray(months.names) || !Array.isArray(months.lengths) || months.names.length !== months.lengths.length || months.names.length === 0 || !months.names.every(isString))) return null;
4351
4356
  if (cfg.eras != null && !Array.isArray(cfg.eras)) return null;
@@ -4357,48 +4362,100 @@ function readCalendarConfig(value) {
4357
4362
  const week = cfg.bundles?.week;
4358
4363
  if (week != null && (!isPositiveInt(week.length) || !Array.isArray(week.names) || !week.names.every(isString))) return null;
4359
4364
  if (cfg.intercalary?.some((i) => i.every_years != null && !isPositiveInt(i.every_years))) return null;
4365
+ if (cfg.version === 2 && cfg.intercalary?.some((i) => leapRuleProblem(i) != null)) return null;
4360
4366
  return cfg;
4361
4367
  }
4368
+ function leapRuleProblem(ic) {
4369
+ const ex = ic.except;
4370
+ const off = ic.offset;
4371
+ if (ex == null && off == null) return null;
4372
+ if (ic.every_years == null) return "except and offset need every_years";
4373
+ if (off != null && !Number.isSafeInteger(off)) return "offset must be a whole number";
4374
+ if (ex != null) {
4375
+ if (!Array.isArray(ex)) return "except must be a list";
4376
+ let prev = ic.every_years;
4377
+ for (const d of ex) {
4378
+ if (!isPositiveInt(d) || d <= prev || d % prev !== 0) {
4379
+ return "each except must be a multiple of the number before it, and larger";
4380
+ }
4381
+ prev = d;
4382
+ }
4383
+ }
4384
+ return null;
4385
+ }
4362
4386
  function isString(s) {
4363
4387
  return typeof s === "string";
4364
4388
  }
4365
4389
  function isPositiveInt(n) {
4366
4390
  return Number.isSafeInteger(n) && n >= 1;
4367
4391
  }
4392
+ function leapRule(cfg, ic) {
4393
+ const v2 = cfg.version === 2;
4394
+ return { n: ic.every_years, except: v2 && ic.except || [], offset: v2 && ic.offset || 0 };
4395
+ }
4396
+ function leapRules(cfg, inter) {
4397
+ return inter.filter((i) => i.every_years != null).map((i) => leapRule(cfg, i));
4398
+ }
4368
4399
  function yearShape(cfg) {
4369
4400
  const months = cfg.bundles?.months;
4370
4401
  const inter = cfg.intercalary ?? [];
4371
- const leapPeriods = inter.filter((i) => i.every_years != null).map((i) => i.every_years);
4402
+ const rules = leapRules(cfg, inter);
4372
4403
  if (months) {
4373
4404
  const base = months.lengths.reduce((a, b) => a + b, 0) + inter.filter((i) => i.every_years == null).length;
4374
- return { baseLen: base, leapPeriods };
4405
+ return { baseLen: base, rules };
4375
4406
  }
4376
- return { baseLen: cfg.bundles?.year?.steps ?? 1, leapPeriods };
4407
+ return { baseLen: cfg.bundles?.year?.steps ?? 1, rules };
4408
+ }
4409
+ function ruleHolds(r, y) {
4410
+ const Y = y + r.offset;
4411
+ let on = floorMod(Y, r.n) === 0;
4412
+ for (const d of r.except) if (floorMod(Y, d) === 0) on = !on;
4413
+ return on;
4377
4414
  }
4378
- function leapYearsBefore(y, N) {
4379
- if (y > 0) return floorDiv(y - 1, N) + 1;
4380
- if (y === 0) return 0;
4381
- return -floorDiv(-y, N);
4415
+ function multiplesIn(a, b, m, offset) {
4416
+ return floorDiv(b - 1 + offset, m) - floorDiv(a - 1 + offset, m);
4417
+ }
4418
+ function leapYearsBefore(y, r) {
4419
+ const [a, b, sign] = y >= 0 ? [0, y, 1] : [y, 0, -1];
4420
+ let count = multiplesIn(a, b, r.n, r.offset);
4421
+ let s = -1;
4422
+ for (const d of r.except) {
4423
+ count += s * multiplesIn(a, b, d, r.offset);
4424
+ s = -s;
4425
+ }
4426
+ return sign * count;
4382
4427
  }
4383
4428
  function isLeapYear(y, N) {
4384
4429
  return floorMod(y, N) === 0;
4385
4430
  }
4431
+ function intercalaryInYear(cfg, ic, y) {
4432
+ return ic.every_years == null || ruleHolds(leapRule(cfg, ic), y);
4433
+ }
4386
4434
  function yearStartStep(cfg, y) {
4387
4435
  const shape = yearShape(cfg);
4388
4436
  let start = y * shape.baseLen;
4389
- for (const N of shape.leapPeriods) start += leapYearsBefore(y, N);
4437
+ for (const r of shape.rules) start += leapYearsBefore(y, r);
4390
4438
  return start;
4391
4439
  }
4392
4440
  function yearLength(cfg, y) {
4393
4441
  const shape = yearShape(cfg);
4394
4442
  let len = shape.baseLen;
4395
- for (const N of shape.leapPeriods) if (isLeapYear(y, N)) len += 1;
4443
+ for (const r of shape.rules) if (ruleHolds(r, y)) len += 1;
4396
4444
  return len;
4397
4445
  }
4446
+ function meanLeap(r) {
4447
+ let m = 1 / r.n;
4448
+ let s = -1;
4449
+ for (const d of r.except) {
4450
+ m += s / d;
4451
+ s = -s;
4452
+ }
4453
+ return m;
4454
+ }
4398
4455
  var MAX_YEAR_CORRECTION = 1e4;
4399
4456
  function absoluteYear(cfg, step) {
4400
4457
  const shape = yearShape(cfg);
4401
- const meanLen = shape.leapPeriods.reduce((a, N) => a + 1 / N, shape.baseLen);
4458
+ const meanLen = shape.rules.reduce((a, r) => a + meanLeap(r), shape.baseLen);
4402
4459
  let y = floorDiv(step, meanLen);
4403
4460
  if (!Number.isFinite(y)) return y;
4404
4461
  for (let i = 0; i < MAX_YEAR_CORRECTION && step < yearStartStep(cfg, y); i++) y -= 1;
@@ -4413,8 +4470,8 @@ function resolveEraDomains(eras) {
4413
4470
  if ((era.direction ?? "up") === "down") {
4414
4471
  out.push({ era, lo: era.end ?? -Infinity, hi: era.start });
4415
4472
  } else {
4416
- const nextStart = sorted[i + 1]?.start;
4417
- out.push({ era, lo: era.start, hi: era.end ?? nextStart ?? Infinity });
4473
+ const nextUp = sorted.slice(i + 1).find((e) => (e.direction ?? "up") === "up");
4474
+ out.push({ era, lo: era.start, hi: era.end ?? nextUp?.start ?? Infinity });
4418
4475
  }
4419
4476
  }
4420
4477
  return out;
@@ -4442,7 +4499,7 @@ function yearSlots(cfg, y) {
4442
4499
  const months = cfg.bundles?.months;
4443
4500
  if (!months) return [];
4444
4501
  const inter = cfg.intercalary ?? [];
4445
- const active = (ic) => ic.every_years == null || isLeapYear(y, ic.every_years);
4502
+ const active = (ic) => intercalaryInYear(cfg, ic, y);
4446
4503
  const slots = [];
4447
4504
  const push = (afterMonth) => {
4448
4505
  for (const ic of inter) if (ic.after_month === afterMonth && active(ic)) slots.push({ kind: "intercalary", intercalary: ic });
@@ -4458,7 +4515,7 @@ function skippedWeekDaysPerYear(cfg) {
4458
4515
  const inter = (cfg.intercalary ?? []).filter((i) => i.in_week === false);
4459
4516
  return {
4460
4517
  base: inter.filter((i) => i.every_years == null).length,
4461
- leapPeriods: inter.filter((i) => i.every_years != null).map((i) => i.every_years)
4518
+ rules: leapRules(cfg, inter)
4462
4519
  };
4463
4520
  }
4464
4521
  function weekdayForStep(cfg, step, pre) {
@@ -4473,7 +4530,7 @@ function weekdayForStep(cfg, step, pre) {
4473
4530
  const skipped = skippedWeekDaysPerYear(cfg);
4474
4531
  const y = pos.absYear;
4475
4532
  let skippedBeforeYear = y * skipped.base;
4476
- for (const N of skipped.leapPeriods) skippedBeforeYear += leapYearsBefore(y, N);
4533
+ for (const r of skipped.rules) skippedBeforeYear += leapYearsBefore(y, r);
4477
4534
  let skippedInYear = 0;
4478
4535
  if (pos.slots) {
4479
4536
  for (let i = 0; i < pos.offset; i++) {
@@ -4565,7 +4622,7 @@ function monthStartSteps(cfg, y) {
4565
4622
  const months = cfg.bundles?.months;
4566
4623
  if (!months) return [];
4567
4624
  const inter = cfg.intercalary ?? [];
4568
- const active = (ic) => ic.every_years == null || isLeapYear(y, ic.every_years);
4625
+ const active = (ic) => intercalaryInYear(cfg, ic, y);
4569
4626
  const out = [];
4570
4627
  let offset = inter.filter((i) => i.after_month === 0 && active(i)).length;
4571
4628
  const start = yearStartStep(cfg, y);
@@ -4983,6 +5040,7 @@ export {
4983
5040
  formatYearLabel,
4984
5041
  getElementIcon,
4985
5042
  getElementLabel,
5043
+ intercalaryInYear,
4986
5044
  isAgentDoor,
4987
5045
  isDemoKey,
4988
5046
  isKnownOf,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlyworlds/sdk",
3
- "version": "4.75.0",
3
+ "version": "4.76.1",
4
4
  "description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",
5
5
  "type": "module",
6
6
  "exports": {