@onlyworlds/sdk 4.18.1 → 4.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -2
- package/CHANGELOG.md +35 -0
- package/README.md +18 -0
- package/dist/index.d.ts +274 -3
- package/dist/index.js +589 -9
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -43,8 +43,10 @@ against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum)
|
|
|
43
43
|
to the edge with a single-use ticket from keel, never with the API key. `removeImage(urlOrKey)`
|
|
44
44
|
takes one back (its uploader or the world's owner); it leaves `image_url` on elements as is.
|
|
45
45
|
- Ask (since 4.13.0, account token): `buildAskContext(hits, index)` then
|
|
46
|
-
`account.ask({ worldId, question, context })`; answers only from the context.
|
|
47
|
-
account (`
|
|
46
|
+
`account.ask({ worldId, question, context })`; answers only from the context. Spends from the
|
|
47
|
+
account's daily token rating (`tokens_remaining`, `token_rating`; `remaining` counts answers of
|
|
48
|
+
this size still in it); used up → `err.isQuotaExceeded`. `isAskUnavailable`, `isAskFailed`
|
|
49
|
+
count nothing. Custom fields (`x_*`) are in the lookup and the context; `atlas_*` is not.
|
|
48
50
|
- Sort (since 4.18.0, account token): `account.sort({ worldId, notes, slice })` turns unsorted
|
|
49
51
|
notes (text, or a picture already uploaded into the world) into rows to review: `make`,
|
|
50
52
|
`add_to`, `link`, `ask`, plus `not_proposed` with reasons. Ids come only from your `slice`
|
|
@@ -61,6 +63,10 @@ against live data). The v1 surface (`OnlyWorldsClient`, the `ElementType` enum)
|
|
|
61
63
|
world folder and returns `{ worldId, name, world, files: [{ path, data }] }`;
|
|
62
64
|
`writeWorldFile(files, topDir)` makes one. Refusals throw `OwWorldFileError` with a `reason`.
|
|
63
65
|
`world.json`'s `api` block (link and key) is removed both ways.
|
|
66
|
+
- World calendars (since 4.20.0): `worldCalendars(elements)` finds the elements carrying
|
|
67
|
+
`atlas_calendar` (Atlas owns the field: read it, never write it); `resolveCalendarLens` picks
|
|
68
|
+
one; `formatStep(config, step)` names a point on the world's integer time line, `parseDate`
|
|
69
|
+
reads one back. No calendar or a broken one is null: show the bare number.
|
|
64
70
|
- Ids: the client mints **UUIDv7** on an id-less `create` (since 4.2.0; keel mints v7 too).
|
|
65
71
|
A v4 or v7 id you supply is accepted. **Never sort elements by id**: worlds mix v7, v4
|
|
66
72
|
and legacy `06x…` ids (nibble 7 too, but seconds-first). For creation order use
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ earlier history lives in git log only.
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [4.20.0] — 2026-10-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **World calendars**, Atlas's calendar code in the SDK so Lantern can name dates (Temper,
|
|
12
|
+
h37 #4284): `formatStep`, `formatYearLabel`, `breakdownStep`, `parseDate` and the rest of
|
|
13
|
+
Atlas's `src/core/calendar.ts` (Atlas 02d6a6d), plus `worldCalendars`,
|
|
14
|
+
`resolveCalendarLens` and `readCalendarValue` from its `calendar-lens.ts`, on any element
|
|
15
|
+
shape. `CALENDAR_FIELD` is `atlas_calendar`: Atlas owns the field, its meaning and its
|
|
16
|
+
writes, and other tools read it (Skeld, h37 #4286).
|
|
17
|
+
- `src/calendar/calendar.ts` is Atlas's file byte for byte, recorded in
|
|
18
|
+
`src/calendar/SOURCES.json` (Assembly's copy-drift); `codegen/atlas-calendar.mjs` re-copies it
|
|
19
|
+
and ports Atlas's 48 kernel tests (TypeScript's transpiler, a five-matcher `test/expect.mjs`
|
|
20
|
+
over node:test). Watched failing: a planted `floorMod` edit failed two tests and the record
|
|
21
|
+
(EDITED); a lens edit failed its test. An app that imports none of it bundles none of it.
|
|
22
|
+
|
|
23
|
+
## [4.19.0] — 2026-10-14
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **`tokens_remaining` and `token_rating` on `OwAskAnswer` and `OwSortAnswer`**: since keel
|
|
27
|
+
a6244db (Skeld, h37 #4233) ask and sort spend from the account's daily token rating, one
|
|
28
|
+
meter with the Council. `remaining` stays a count of answers: how many more calls of this
|
|
29
|
+
size fit in what is left today (keel 504437b), never tokens. Typed from keel's code
|
|
30
|
+
(`accounts/ask.py`), since its OpenAPI document gives these answers no schema.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- **Custom fields (`x_*`) are in the lookup and on the card.** `buildWorldIndex` finds an
|
|
34
|
+
element by its custom text and numbers, and `renderElement` (so `buildAskContext`) shows
|
|
35
|
+
each one by its own name: `x_eye_colour` reads `eye colour: grey`; an id in one is a link,
|
|
36
|
+
by name; a long value is cut like a description. `atlas_*` machinery stays out, and so do
|
|
37
|
+
object values (a tool's state). Lantern shows and edits custom fields now, so Ask can answer
|
|
38
|
+
what find already could (Temper, h37 #4069). The trial's lookup (`dossiers/lantern-ask`)
|
|
39
|
+
still skips them; parity holds on its ten questions, whose fixture has no custom text.
|
|
40
|
+
- The lookup's stopword set is an array literal, so bundlers mark it pure and drop it from
|
|
41
|
+
apps that never call the lookup (451 bytes; Temper, h17 #4247).
|
|
42
|
+
|
|
8
43
|
## [4.18.1] — 2026-10-12
|
|
9
44
|
|
|
10
45
|
### Changed
|
package/README.md
CHANGED
|
@@ -198,6 +198,24 @@ const zip = writeWorldFile(world.files, 'hollow-reach-0192a000'); // one top-lev
|
|
|
198
198
|
|
|
199
199
|
The reader refuses a file before returning anything: a path that could land outside the folder, more than 1 GiB or 200,000 files uncompressed (`maxBytes`, `maxEntries` to change them), no `world.json` with an `id`, or more than one world. Check `err.reason` on the `OwWorldFileError` it throws. The world's OnlyWorlds link and key (`world.json`'s `api`) never travel: both functions remove them. A tool's own folder (`.atlas/` and the like) comes back like any other file; pass `skip: path => …` to leave files out.
|
|
200
200
|
|
|
201
|
+
## World calendars
|
|
202
|
+
|
|
203
|
+
A world's time is an integer line (`time_range_current` is a point on it, and so is an Event's `start_date`). A calendar names its points: months, days, eras. Atlas keeps a calendar on an element (a Construct by convention) in its `atlas_calendar` field, and the SDK reads it with Atlas's own calendar code (since 4.20.0). Atlas owns that field and is its only writer; read it, never write it.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { worldCalendars, resolveCalendarLens, formatStep, formatYearLabel, parseDate } from '@onlyworlds/sdk';
|
|
207
|
+
|
|
208
|
+
const calendars = worldCalendars(elements); // [{ id, name, config }]: every element with a valid atlas_calendar
|
|
209
|
+
const cal = resolveCalendarLens(elements, chosenId, { fallbackToOnly: true }); // a world with one calendar uses it
|
|
210
|
+
if (cal) {
|
|
211
|
+
formatStep(cal.config, 400); // "4 Alturiak, 1 DR" (with Harptos, from Atlas's tests)
|
|
212
|
+
formatYearLabel(cal.config, 400); // "1 DR"
|
|
213
|
+
parseDate(cal.config, '4 Alturiak, 1 DR'); // { step: 400 }; a bare number is always the raw step
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
No calendar, or a broken one, gives null: show the bare number. `breakdownStep` gives the parts (year, era, month, day, weekday, an intercalary day). The rules are Atlas's calendar design, §5 and §5b.
|
|
218
|
+
|
|
201
219
|
## Errors
|
|
202
220
|
|
|
203
221
|
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. A write sent with `ifMatch` that found the element moved throws `OwPreconditionFailedError` (412, `err.isPreconditionFailed`), an `OwApiError` that also carries `current`.
|
package/dist/index.d.ts
CHANGED
|
@@ -2009,8 +2009,13 @@ interface OwAskAnswer {
|
|
|
2009
2009
|
input_tokens: number;
|
|
2010
2010
|
output_tokens: number;
|
|
2011
2011
|
};
|
|
2012
|
-
/**
|
|
2012
|
+
/** How many more calls of this one's size fit in what the account has left today (a count of
|
|
2013
|
+
* answers, never tokens; it never over-promises). */
|
|
2013
2014
|
remaining: number;
|
|
2015
|
+
/** The account's tokens left today after this call (since keel a6244db, 2026-10-14). */
|
|
2016
|
+
tokens_remaining: number;
|
|
2017
|
+
/** The account's daily allowance in tokens, one meter with the Council (since keel a6244db). */
|
|
2018
|
+
token_rating: number;
|
|
2014
2019
|
[k: string]: unknown;
|
|
2015
2020
|
}
|
|
2016
2021
|
/**
|
|
@@ -2090,8 +2095,13 @@ interface OwSortAnswer {
|
|
|
2090
2095
|
input_tokens: number;
|
|
2091
2096
|
output_tokens: number;
|
|
2092
2097
|
};
|
|
2093
|
-
/**
|
|
2098
|
+
/** How many more calls of this one's size fit in what the account has left today; ask and
|
|
2099
|
+
* sort spend from one allowance. */
|
|
2094
2100
|
remaining: number;
|
|
2101
|
+
/** The account's tokens left today after this call (since keel a6244db, 2026-10-14). */
|
|
2102
|
+
tokens_remaining: number;
|
|
2103
|
+
/** The account's daily allowance in tokens, one meter with the Council (since keel a6244db). */
|
|
2104
|
+
token_rating: number;
|
|
2095
2105
|
[k: string]: unknown;
|
|
2096
2106
|
}
|
|
2097
2107
|
/** POST /join/preview: what a join code holds, read without using it (keel #212). */
|
|
@@ -2979,6 +2989,267 @@ declare function planPhoto(source: {
|
|
|
2979
2989
|
*/
|
|
2980
2990
|
declare function preparePhoto(input: Blob, opts?: OwPhotoOptions): Promise<OwPreparedPhoto>;
|
|
2981
2991
|
|
|
2992
|
+
/**
|
|
2993
|
+
* Calendar kernel — pure arithmetic, formatting, and parsing over the world's
|
|
2994
|
+
* integer time line.
|
|
2995
|
+
*
|
|
2996
|
+
* Contract: Forge/Notes/2026-07-14-calendar-design-final.md §5 + §5b (3-lens
|
|
2997
|
+
* gated 2026-07-14). The binding rules, restated where they bite:
|
|
2998
|
+
*
|
|
2999
|
+
* - The number line is the only truth. A calendar names it; it never owns it.
|
|
3000
|
+
* - Absolute year index is ZERO-based: step 0 opens absolute year 0. All
|
|
3001
|
+
* division is Euclidean floor (negatives depend on it).
|
|
3002
|
+
* - format(step) is total and O(config) — it never walks history.
|
|
3003
|
+
* - Intercalary days belong to no month and render via `format_intercalary`.
|
|
3004
|
+
* - A KNOWN token with no applicable value renders empty (with separator
|
|
3005
|
+
* collapse); an UNKNOWN token renders literally.
|
|
3006
|
+
* - parse() matches calendar words case-insensitively; bare numbers are ALWAYS
|
|
3007
|
+
* the raw stored step. Guaranteed property: format(parse(format(n))) ===
|
|
3008
|
+
* format(n). Exact integer recovery is NOT promised under lossy formats.
|
|
3009
|
+
*
|
|
3010
|
+
* Everything here is pure. No atlas imports beyond nothing — this module is
|
|
3011
|
+
* consumed by the charts room, popovers, and (via freeze) the publish path.
|
|
3012
|
+
*/
|
|
3013
|
+
interface CalendarEra {
|
|
3014
|
+
name: string;
|
|
3015
|
+
abbrev?: string;
|
|
3016
|
+
/** Up-era: domain start (inclusive). Down-era: the PIVOT (exclusive upper bound). */
|
|
3017
|
+
start: number;
|
|
3018
|
+
/** Up-era: domain end (exclusive); defaults to the next era's start. Down-era: lower bound (inclusive); defaults to -infinity. */
|
|
3019
|
+
end?: number;
|
|
3020
|
+
/** true (default): years number from `start`. false: pure label, {year} shows the absolute year index. */
|
|
3021
|
+
restart?: boolean;
|
|
3022
|
+
/** First year number of a restart era: 0 or 1 (default 1). */
|
|
3023
|
+
year_base?: 0 | 1;
|
|
3024
|
+
/** 'up' (default) or 'down' (BC / Dune B.G. — counts toward the pivot). */
|
|
3025
|
+
direction?: 'up' | 'down';
|
|
3026
|
+
/** Closed eras format old dates but are never offered at date entry. */
|
|
3027
|
+
closed?: boolean;
|
|
3028
|
+
}
|
|
3029
|
+
interface CalendarMonths {
|
|
3030
|
+
names: string[];
|
|
3031
|
+
/** Same length as names. */
|
|
3032
|
+
lengths: number[];
|
|
3033
|
+
}
|
|
3034
|
+
interface CalendarWeek {
|
|
3035
|
+
length: number;
|
|
3036
|
+
names: string[];
|
|
3037
|
+
/** 'free' (default): runs across month boundaries, anchor step 0 = names[0].
|
|
3038
|
+
* 'nested': weekday = (day_of_month - 1) mod length; ragged final week legal. */
|
|
3039
|
+
mode?: 'free' | 'nested';
|
|
3040
|
+
}
|
|
3041
|
+
interface CalendarIntercalary {
|
|
3042
|
+
name: string;
|
|
3043
|
+
/** 0 = before month 1; k = after month k. Requires months. */
|
|
3044
|
+
after_month: number;
|
|
3045
|
+
/** Present only in years where floorMod(absYear, N) === 0 (the leap rule). */
|
|
3046
|
+
every_years?: number;
|
|
3047
|
+
/** Counts in the free-running week (default true). The Shire lock = opting
|
|
3048
|
+
* exactly Mid-year's Day + Overlithe out (364 = 52 × 7). */
|
|
3049
|
+
in_week?: boolean;
|
|
3050
|
+
}
|
|
3051
|
+
interface CalendarConfig {
|
|
3052
|
+
version: number;
|
|
3053
|
+
/** Immutable sentinel: the world's time_basic_unit at creation. Machinery only. */
|
|
3054
|
+
grain: string;
|
|
3055
|
+
/** The culture's display word for one step ("Sol"). Defaults to grain. */
|
|
3056
|
+
step_name?: string;
|
|
3057
|
+
eras?: CalendarEra[];
|
|
3058
|
+
bundles?: {
|
|
3059
|
+
/** Coarse-calendar year length. Legal ONLY when months are absent. */
|
|
3060
|
+
year?: {
|
|
3061
|
+
steps: number;
|
|
3062
|
+
};
|
|
3063
|
+
months?: CalendarMonths;
|
|
3064
|
+
week?: CalendarWeek;
|
|
3065
|
+
};
|
|
3066
|
+
intercalary?: CalendarIntercalary[];
|
|
3067
|
+
/** Regular-day template. Tokens: {day} {month} {weekday} {year} {era} {step}. */
|
|
3068
|
+
format?: string;
|
|
3069
|
+
/** Intercalary-day template; may also use {intercalary}. */
|
|
3070
|
+
format_intercalary?: string;
|
|
3071
|
+
}
|
|
3072
|
+
/** A step, taken apart. Fields are null when the structure doesn't exist. */
|
|
3073
|
+
interface StepBreakdown {
|
|
3074
|
+
step: number;
|
|
3075
|
+
/** Absolute year index (zero-based from step 0). */
|
|
3076
|
+
absYear: number;
|
|
3077
|
+
/** The era-local display year (restart arithmetic applied), or absYear when no era / restart:false. */
|
|
3078
|
+
year: number;
|
|
3079
|
+
era: CalendarEra | null;
|
|
3080
|
+
/** 1-based month index, or null (intercalary day / no months). */
|
|
3081
|
+
month: number | null;
|
|
3082
|
+
monthName: string | null;
|
|
3083
|
+
/** 1-based day within the month, or null. */
|
|
3084
|
+
day: number | null;
|
|
3085
|
+
/** Intercalary day name when the step IS one, else null. */
|
|
3086
|
+
intercalary: string | null;
|
|
3087
|
+
weekday: string | null;
|
|
3088
|
+
}
|
|
3089
|
+
interface ParseResult {
|
|
3090
|
+
step: number;
|
|
3091
|
+
}
|
|
3092
|
+
interface ParseError {
|
|
3093
|
+
error: string;
|
|
3094
|
+
}
|
|
3095
|
+
declare function floorDiv(a: number, b: number): number;
|
|
3096
|
+
declare function floorMod(a: number, b: number): number;
|
|
3097
|
+
declare function validateCalendarConfig(cfg: CalendarConfig): string[];
|
|
3098
|
+
/**
|
|
3099
|
+
* Defensive reader for wild configs (hand-edited files, other tools). Returns
|
|
3100
|
+
* null when the value is not a structurally usable calendar — callers fall
|
|
3101
|
+
* back to bare numbers, never a broken axis.
|
|
3102
|
+
*/
|
|
3103
|
+
declare function readCalendarConfig(value: unknown): CalendarConfig | null;
|
|
3104
|
+
/** True when absolute year y contains the leap entry with period N. */
|
|
3105
|
+
declare function isLeapYear(y: number, N: number): boolean;
|
|
3106
|
+
/** First step of absolute year y. yearStart(0) === 0 by construction. */
|
|
3107
|
+
declare function yearStartStep(cfg: CalendarConfig, y: number): number;
|
|
3108
|
+
/** Length in steps of absolute year y. */
|
|
3109
|
+
declare function yearLength(cfg: CalendarConfig, y: number): number;
|
|
3110
|
+
/** Absolute year index of a step. Zero-based; closed-form seed + bounded correction. */
|
|
3111
|
+
declare function absoluteYear(cfg: CalendarConfig, step: number): number;
|
|
3112
|
+
interface EraDomain {
|
|
3113
|
+
era: CalendarEra;
|
|
3114
|
+
lo: number;
|
|
3115
|
+
hi: number;
|
|
3116
|
+
}
|
|
3117
|
+
declare function resolveEraDomains(eras: CalendarEra[]): EraDomain[];
|
|
3118
|
+
/** The era owning a step: domain containment; wild overlaps resolve to latest start (up wins ties). */
|
|
3119
|
+
declare function eraForStep(cfg: CalendarConfig, step: number): CalendarEra | null;
|
|
3120
|
+
/** The display year for a step under an era (or the absolute year without one). */
|
|
3121
|
+
declare function displayYear(cfg: CalendarConfig, step: number, era: CalendarEra | null): number;
|
|
3122
|
+
interface DaySlot {
|
|
3123
|
+
kind: 'month' | 'intercalary';
|
|
3124
|
+
month?: number;
|
|
3125
|
+
day?: number;
|
|
3126
|
+
intercalary?: CalendarIntercalary;
|
|
3127
|
+
}
|
|
3128
|
+
/** Weekday name for a step, or null (no week / nested outside months / skipped intercalary day). */
|
|
3129
|
+
declare function weekdayForStep(cfg: CalendarConfig, step: number, pre?: StepPosition): string | null;
|
|
3130
|
+
interface StepPosition {
|
|
3131
|
+
absYear: number;
|
|
3132
|
+
offset: number;
|
|
3133
|
+
slot: DaySlot | null;
|
|
3134
|
+
slots: DaySlot[] | null;
|
|
3135
|
+
}
|
|
3136
|
+
declare function breakdownStep(cfg: CalendarConfig, step: number): StepBreakdown;
|
|
3137
|
+
/** Format a step. Total: every integer formats to a string under every readable config. */
|
|
3138
|
+
declare function formatStep(cfg: CalendarConfig, step: number): string;
|
|
3139
|
+
/**
|
|
3140
|
+
* Compact year-level label: "{year} {era-abbrev}" — the axis-tick wording when
|
|
3141
|
+
* zoomed wide (full formatStep at tick density would be a wall of month names).
|
|
3142
|
+
* Outside every era: the absolute year index alone.
|
|
3143
|
+
*/
|
|
3144
|
+
declare function formatYearLabel(cfg: CalendarConfig, step: number): string;
|
|
3145
|
+
/**
|
|
3146
|
+
* The base year length in steps (leap days excluded), or null when the
|
|
3147
|
+
* calendar has no year structure above the step. Consumers use it as a
|
|
3148
|
+
* zoom threshold (ticks tighter than a year → full dates, else year labels).
|
|
3149
|
+
*/
|
|
3150
|
+
declare function approxYearLength(cfg: CalendarConfig): number | null;
|
|
3151
|
+
/**
|
|
3152
|
+
* A duration in steps, spoken: "19 years" when the calendar has year
|
|
3153
|
+
* structure and the span reaches it, else "12 days" in the step word. Uses
|
|
3154
|
+
* the BASE year length (leap days are noise at duration granularity); one
|
|
3155
|
+
* decimal below 10 years, whole above. Pure.
|
|
3156
|
+
*/
|
|
3157
|
+
declare function formatDuration(cfg: CalendarConfig, steps: number): string;
|
|
3158
|
+
/**
|
|
3159
|
+
* The first step of each month in absolute year y, in order. O(config) — walks
|
|
3160
|
+
* month lengths + intercalary placement, never the year's days. Empty when the
|
|
3161
|
+
* calendar has no months.
|
|
3162
|
+
*/
|
|
3163
|
+
declare function monthStartSteps(cfg: CalendarConfig, y: number): {
|
|
3164
|
+
month: number;
|
|
3165
|
+
name: string;
|
|
3166
|
+
step: number;
|
|
3167
|
+
}[];
|
|
3168
|
+
/**
|
|
3169
|
+
* Mid-rung label for dense axis ticks: "11 Greening 454" — day, month, display
|
|
3170
|
+
* year, NO era word (the era bands carry the era; a label wall repeating "FT"
|
|
3171
|
+
* is noise). Intercalary steps read "Hearthmoot 454". Monthless calendars fall
|
|
3172
|
+
* back to the year label.
|
|
3173
|
+
*/
|
|
3174
|
+
declare function formatMidLabel(cfg: CalendarConfig, step: number): string;
|
|
3175
|
+
interface ParseOptions {
|
|
3176
|
+
/** The world's now (used to default a missing year, per contract rule 9). */
|
|
3177
|
+
now?: number | null;
|
|
3178
|
+
}
|
|
3179
|
+
/** Absolute year for an era-local display year (inverse of displayYear). */
|
|
3180
|
+
declare function absYearForEraYear(cfg: CalendarConfig, era: CalendarEra, Y: number): number;
|
|
3181
|
+
/**
|
|
3182
|
+
* Parse a written date to a step.
|
|
3183
|
+
*
|
|
3184
|
+
* Accepted shapes (calendar words case-insensitive):
|
|
3185
|
+
* "-454" / "454" -> the raw stored step, always
|
|
3186
|
+
* "454 AE" / "Year 12 FT" -> year (+ era)
|
|
3187
|
+
* "Frostwane 454" / "12 Frostwane 454" (+ era)
|
|
3188
|
+
* "Shieldmeet 1372" (+ era) -> intercalary day
|
|
3189
|
+
* "Frostwane 12" (no year) -> resolved against the now's year
|
|
3190
|
+
* Partial dates snap to the period's first step. Missing year with no now =
|
|
3191
|
+
* error (never guess). p(f(n)) == n is NOT promised; f(p(f(n))) == f(n) is.
|
|
3192
|
+
*/
|
|
3193
|
+
declare function parseDate(cfg: CalendarConfig, input: string, opts?: ParseOptions): ParseResult | ParseError;
|
|
3194
|
+
declare function isParseError(r: ParseResult | ParseError): r is ParseError;
|
|
3195
|
+
|
|
3196
|
+
/**
|
|
3197
|
+
* World calendars: Atlas's calendar kernel, and which calendars a world holds.
|
|
3198
|
+
*
|
|
3199
|
+
* `calendar.ts` beside this file is Atlas's `src/core/calendar.ts`, byte for byte
|
|
3200
|
+
* (Forge/tools/atlas; recorded in `SOURCES.json`, checked with Assembly's copy-drift).
|
|
3201
|
+
* Never edit it here: change Atlas's, then copy and re-record. This file adapts Atlas's
|
|
3202
|
+
* `src/core/calendar-lens.ts` (readCalendarValue, worldCalendars, resolveCalendarLens) to
|
|
3203
|
+
* any element shape, since the SDK has no Atlas `Element`.
|
|
3204
|
+
*
|
|
3205
|
+
* The field: a calendar is an element (a Construct by convention) carrying `atlas_calendar`.
|
|
3206
|
+
* Atlas owns the field, its meaning and its writes (its calendar design, §5 and §5b); every
|
|
3207
|
+
* other tool reads it and never writes it (Skeld, h37 #4286: a prefix names the owner, not
|
|
3208
|
+
* who may read). Detection keys on the field's presence, never on the supertype.
|
|
3209
|
+
* Everything degrades to bare numbers: a missing, dangling or malformed calendar is null,
|
|
3210
|
+
* never an error.
|
|
3211
|
+
*/
|
|
3212
|
+
|
|
3213
|
+
/** The supertype Atlas gives a calendar Construct: convention and display only. */
|
|
3214
|
+
declare const CALENDAR_SUPERTYPE = "calendar";
|
|
3215
|
+
/** Atlas's field for a calendar's config (owned by Atlas: read it, never write it). */
|
|
3216
|
+
declare const CALENDAR_FIELD = "atlas_calendar";
|
|
3217
|
+
/** Any element: an id, a name, and the rest of its fields. */
|
|
3218
|
+
interface OwCalendarElement {
|
|
3219
|
+
id: string;
|
|
3220
|
+
name?: string | null;
|
|
3221
|
+
[field: string]: unknown;
|
|
3222
|
+
}
|
|
3223
|
+
interface WorldCalendar {
|
|
3224
|
+
/** The calendar element's id. */
|
|
3225
|
+
id: string;
|
|
3226
|
+
/** The element's name: the calendar's display name. */
|
|
3227
|
+
name: string;
|
|
3228
|
+
config: CalendarConfig;
|
|
3229
|
+
}
|
|
3230
|
+
/**
|
|
3231
|
+
* The tolerant `atlas_calendar` read: an object or a JSON string (another tool may
|
|
3232
|
+
* serialize it; Atlas writes objects). Null for anything unusable. Read the field only
|
|
3233
|
+
* through this: a reader that took objects alone would blank a string config.
|
|
3234
|
+
*/
|
|
3235
|
+
declare function readCalendarValue(raw: unknown): CalendarConfig | null;
|
|
3236
|
+
/**
|
|
3237
|
+
* Every usable calendar in the world: the elements with a valid `atlas_calendar`.
|
|
3238
|
+
* A malformed config is skipped: a broken calendar is invisible, not an error.
|
|
3239
|
+
*/
|
|
3240
|
+
declare function worldCalendars(elements: Iterable<OwCalendarElement>): WorldCalendar[];
|
|
3241
|
+
/**
|
|
3242
|
+
* Resolve a chosen calendar id against the elements. A null id, a dangling id or an
|
|
3243
|
+
* invalid config resolve to null: bare numbers.
|
|
3244
|
+
*
|
|
3245
|
+
* `fallbackToOnly`: when no calendar was ever chosen (`activeId` is `undefined`, not
|
|
3246
|
+
* `null`) and the world holds exactly one, use it. `null` is an explicit "no calendar" and
|
|
3247
|
+
* stays null. With several calendars and no choice, nothing is guessed.
|
|
3248
|
+
*/
|
|
3249
|
+
declare function resolveCalendarLens(elements: Iterable<OwCalendarElement>, activeId: string | null | undefined, opts?: {
|
|
3250
|
+
fallbackToOnly?: boolean;
|
|
3251
|
+
}): WorldCalendar | null;
|
|
3252
|
+
|
|
2982
3253
|
/**
|
|
2983
3254
|
* OnlyWorlds Token Management API Types
|
|
2984
3255
|
*
|
|
@@ -3263,4 +3534,4 @@ declare class TokenResource {
|
|
|
3263
3534
|
getEncryptionInfo(): Promise<EncryptionInfo>;
|
|
3264
3535
|
}
|
|
3265
3536
|
|
|
3266
|
-
export { ASK_LIMITS, type AccessKeyResponse, ELEMENT_FAMILIES, ELEMENT_ICONS, ELEMENT_LABELS, ELEMENT_SECTIONS, ELEMENT_TYPES, type ElementFamily, type ElementType, type EncryptionInfo, FAMILY_COLORS, FAMILY_ORDER, FIELD_SCHEMA, type FieldInfo, type FieldType, GameTier, type ListParams, ONLYWORLDS_VERSION, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwChange, type OwChangesPage, type OwClientConfig, 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 OwMediaTicket, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, 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 OwWorldMeta, PHOTO_MAX_BYTES, PHOTO_TYPES, type RevokeAllSessionsResponse, type RevokeSessionResponse, SPATIAL_TYPES, type SectionInfo, type TokenConsumeParams, type TokenConsumeResponse, TokenResource, type TokenStatus, type TokenTransport, WORLD_FILE_MAX_BYTES, WORLD_FILE_MAX_ENTRIES, buildAskContext, buildWorldIndex, detectKeyKind, elementColor, errorFromResponse, familyOf, getElementIcon, getElementLabel, isDemoKey, kindCanWrite, lookupWorld, parseErrorEnvelope, pinExpectation, planPhoto, preparePhoto, readWorldFile, renderElement, writeWorldFile };
|
|
3537
|
+
export { 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, OwApiError, type OwAskAnswer, type OwAskContext, type OwAuthErrorCode, type OwBulkItem, type OwBulkItemResult, type OwBulkResponse, type OwCalendarElement, type OwChange, type OwChangesPage, type OwClientConfig, 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 OwMediaTicket, type OwMemberRole, OwNetworkError, type OwPage, type OwPhotoOptions, type OwPhotoPlan, OwPreconditionFailedError, type OwPreparedPhoto, type OwReadWorldFile, type OwReadWorldFileOptions, type OwRemovalTicket, type OwRemovedImage, type OwRenderOptions, 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 OwWorldMeta, 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, 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
|
@@ -1571,7 +1571,107 @@ function writeWorldFile(files, topDir) {
|
|
|
1571
1571
|
}
|
|
1572
1572
|
|
|
1573
1573
|
// src/lookup.ts
|
|
1574
|
-
var STOP =
|
|
1574
|
+
var STOP = /* @__PURE__ */ new Set([
|
|
1575
|
+
"a",
|
|
1576
|
+
"an",
|
|
1577
|
+
"the",
|
|
1578
|
+
"of",
|
|
1579
|
+
"in",
|
|
1580
|
+
"on",
|
|
1581
|
+
"at",
|
|
1582
|
+
"to",
|
|
1583
|
+
"for",
|
|
1584
|
+
"from",
|
|
1585
|
+
"by",
|
|
1586
|
+
"with",
|
|
1587
|
+
"and",
|
|
1588
|
+
"or",
|
|
1589
|
+
"is",
|
|
1590
|
+
"are",
|
|
1591
|
+
"was",
|
|
1592
|
+
"were",
|
|
1593
|
+
"be",
|
|
1594
|
+
"been",
|
|
1595
|
+
"who",
|
|
1596
|
+
"whom",
|
|
1597
|
+
"whose",
|
|
1598
|
+
"what",
|
|
1599
|
+
"which",
|
|
1600
|
+
"where",
|
|
1601
|
+
"when",
|
|
1602
|
+
"why",
|
|
1603
|
+
"how",
|
|
1604
|
+
"does",
|
|
1605
|
+
"do",
|
|
1606
|
+
"did",
|
|
1607
|
+
"doesn",
|
|
1608
|
+
"has",
|
|
1609
|
+
"have",
|
|
1610
|
+
"had",
|
|
1611
|
+
"it",
|
|
1612
|
+
"its",
|
|
1613
|
+
"this",
|
|
1614
|
+
"that",
|
|
1615
|
+
"these",
|
|
1616
|
+
"those",
|
|
1617
|
+
"there",
|
|
1618
|
+
"their",
|
|
1619
|
+
"they",
|
|
1620
|
+
"them",
|
|
1621
|
+
"he",
|
|
1622
|
+
"she",
|
|
1623
|
+
"his",
|
|
1624
|
+
"her",
|
|
1625
|
+
"him",
|
|
1626
|
+
"i",
|
|
1627
|
+
"me",
|
|
1628
|
+
"my",
|
|
1629
|
+
"we",
|
|
1630
|
+
"our",
|
|
1631
|
+
"you",
|
|
1632
|
+
"your",
|
|
1633
|
+
"about",
|
|
1634
|
+
"tell",
|
|
1635
|
+
"know",
|
|
1636
|
+
"any",
|
|
1637
|
+
"some",
|
|
1638
|
+
"all",
|
|
1639
|
+
"can",
|
|
1640
|
+
"could",
|
|
1641
|
+
"would",
|
|
1642
|
+
"should",
|
|
1643
|
+
"will",
|
|
1644
|
+
"not",
|
|
1645
|
+
"no",
|
|
1646
|
+
"yes",
|
|
1647
|
+
"than",
|
|
1648
|
+
"then",
|
|
1649
|
+
"so",
|
|
1650
|
+
"if",
|
|
1651
|
+
"into",
|
|
1652
|
+
"out",
|
|
1653
|
+
"up",
|
|
1654
|
+
"down",
|
|
1655
|
+
"over",
|
|
1656
|
+
"under",
|
|
1657
|
+
"one",
|
|
1658
|
+
"two",
|
|
1659
|
+
"own",
|
|
1660
|
+
"same",
|
|
1661
|
+
"other",
|
|
1662
|
+
"more",
|
|
1663
|
+
"most",
|
|
1664
|
+
"much",
|
|
1665
|
+
"many",
|
|
1666
|
+
"very",
|
|
1667
|
+
"just",
|
|
1668
|
+
"also",
|
|
1669
|
+
"only",
|
|
1670
|
+
"s",
|
|
1671
|
+
"t",
|
|
1672
|
+
"don",
|
|
1673
|
+
"isn"
|
|
1674
|
+
]);
|
|
1575
1675
|
var SPATIAL = /* @__PURE__ */ new Set(["pin", "marker", "zone", "map"]);
|
|
1576
1676
|
var FIELD_HINTS = {
|
|
1577
1677
|
where: ["location", "parent_location", "birthplace", "locations", "estates", "element_id"],
|
|
@@ -1730,7 +1830,7 @@ function buildWorldIndex(elements) {
|
|
|
1730
1830
|
if (!SPATIAL.has(el.type)) {
|
|
1731
1831
|
const tw = /* @__PURE__ */ new Set();
|
|
1732
1832
|
for (const [field, v] of Object.entries(el)) {
|
|
1733
|
-
if (TEXT_SKIP.has(field) || linkFields.has(field) || field.startsWith("
|
|
1833
|
+
if (TEXT_SKIP.has(field) || linkFields.has(field) || field.startsWith("atlas_")) continue;
|
|
1734
1834
|
const vals = Array.isArray(v) ? v : [v];
|
|
1735
1835
|
for (const x of vals) {
|
|
1736
1836
|
if (typeof x === "number" && Number.isFinite(x)) tw.add(String(x));
|
|
@@ -1908,18 +2008,21 @@ function renderElement(element, index, { descMax = 500, linkMax = 12, incomingMa
|
|
|
1908
2008
|
const unres = /* @__PURE__ */ new Map();
|
|
1909
2009
|
for (const { field } of index.unresolved.get(el.id) || []) unres.set(field, (unres.get(field) || 0) + 1);
|
|
1910
2010
|
for (const [field, v] of Object.entries(el)) {
|
|
1911
|
-
if (RENDER_SKIP.has(field) || field.startsWith("
|
|
2011
|
+
if (RENDER_SKIP.has(field) || field.startsWith("atlas_")) continue;
|
|
2012
|
+
const custom = field.startsWith("x_");
|
|
2013
|
+
const label = custom ? field.slice(2).replace(/_/g, " ") : field;
|
|
1912
2014
|
if (outBy.has(field) || unres.has(field)) {
|
|
1913
2015
|
const names = outBy.get(field) || [];
|
|
1914
|
-
let
|
|
1915
|
-
if (names.length > linkMax)
|
|
2016
|
+
let s2 = names.slice(0, linkMax).join(", ");
|
|
2017
|
+
if (names.length > linkMax) s2 += ` (+${names.length - linkMax} more)`;
|
|
1916
2018
|
const u = unres.get(field);
|
|
1917
|
-
if (u)
|
|
1918
|
-
lines.push(`${
|
|
2019
|
+
if (u) s2 += `${s2 ? "; " : ""}${u} linked element(s) not present in the world data`;
|
|
2020
|
+
lines.push(`${label}: ${s2}`);
|
|
1919
2021
|
continue;
|
|
1920
2022
|
}
|
|
1921
2023
|
if (v === null || v === void 0 || v === "" || Array.isArray(v) && !v.length || typeof v === "object" && !Array.isArray(v)) continue;
|
|
1922
|
-
|
|
2024
|
+
const s = Array.isArray(v) ? v.join(", ") : String(v);
|
|
2025
|
+
lines.push(`${label}: ${custom && s.length > descMax ? s.slice(0, descMax) + "..." : s}`);
|
|
1923
2026
|
}
|
|
1924
2027
|
const inBy = /* @__PURE__ */ new Map();
|
|
1925
2028
|
for (const { field, id } of index.inn.get(el.id) || []) {
|
|
@@ -2032,6 +2135,456 @@ async function preparePhoto(input, opts = {}) {
|
|
|
2032
2135
|
}
|
|
2033
2136
|
}
|
|
2034
2137
|
|
|
2138
|
+
// src/calendar/calendar.ts
|
|
2139
|
+
function floorDiv(a, b) {
|
|
2140
|
+
return Math.floor(a / b);
|
|
2141
|
+
}
|
|
2142
|
+
function floorMod(a, b) {
|
|
2143
|
+
return (a % b + b) % b;
|
|
2144
|
+
}
|
|
2145
|
+
function validateCalendarConfig(cfg) {
|
|
2146
|
+
const errors = [];
|
|
2147
|
+
if (cfg.version !== 1) errors.push(`Unsupported calendar version: ${cfg.version}`);
|
|
2148
|
+
if (!cfg.grain || typeof cfg.grain !== "string") errors.push("Grain is required");
|
|
2149
|
+
const months = cfg.bundles?.months;
|
|
2150
|
+
if (months) {
|
|
2151
|
+
if (!Array.isArray(months.names) || !Array.isArray(months.lengths) || months.names.length === 0 || months.names.length !== months.lengths.length) {
|
|
2152
|
+
errors.push("months.names and months.lengths must be same-length non-empty arrays");
|
|
2153
|
+
} else if (months.lengths.some((l) => !Number.isInteger(l) || l < 1)) {
|
|
2154
|
+
errors.push("Every month length must be a whole number, 1 or more");
|
|
2155
|
+
}
|
|
2156
|
+
if (cfg.bundles?.year) errors.push("year.steps is legal only when no months are defined (year length is derived from months)");
|
|
2157
|
+
} else if (cfg.bundles?.year) {
|
|
2158
|
+
if (!Number.isInteger(cfg.bundles.year.steps) || cfg.bundles.year.steps < 1) {
|
|
2159
|
+
errors.push("year.steps must be a positive integer");
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
const week = cfg.bundles?.week;
|
|
2163
|
+
if (week) {
|
|
2164
|
+
if (!Number.isInteger(week.length) || week.length < 1) errors.push("The week length must be a whole number, 1 or more");
|
|
2165
|
+
else if (!Array.isArray(week.names) || week.names.length !== week.length) errors.push("Give the week as many day names as its length");
|
|
2166
|
+
}
|
|
2167
|
+
const inter = cfg.intercalary ?? [];
|
|
2168
|
+
if (inter.length > 0 && !months) errors.push("Festival days need months");
|
|
2169
|
+
const monthCount = months?.names.length ?? 0;
|
|
2170
|
+
for (const ic of inter) {
|
|
2171
|
+
if (!Number.isInteger(ic.after_month) || ic.after_month < 0 || ic.after_month > monthCount) {
|
|
2172
|
+
errors.push(`Festival day "${ic.name}": pick the month it follows`);
|
|
2173
|
+
}
|
|
2174
|
+
if (ic.every_years != null && (!Number.isInteger(ic.every_years) || ic.every_years < 1)) {
|
|
2175
|
+
errors.push(`Festival day "${ic.name}": every N yrs must be a whole number, 1 or more`);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
const eras = cfg.eras ?? [];
|
|
2179
|
+
for (const era of eras) {
|
|
2180
|
+
if (!era.name) errors.push("Every era needs a name");
|
|
2181
|
+
if (!Number.isFinite(era.start)) errors.push(`Era "${era.name}": start must be a number`);
|
|
2182
|
+
if (era.end != null && !Number.isFinite(era.end)) errors.push(`Era "${era.name}": end must be a number`);
|
|
2183
|
+
if ((era.direction ?? "up") === "up" && era.end != null && era.end <= era.start) {
|
|
2184
|
+
errors.push(`Era "${era.name}": end must be after start`);
|
|
2185
|
+
}
|
|
2186
|
+
if ((era.direction ?? "up") === "down" && era.end != null && era.end >= era.start) {
|
|
2187
|
+
errors.push(`Era "${era.name}": a counting-down era must end below its start`);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
const domains = resolveEraDomains(eras);
|
|
2191
|
+
for (let i = 0; i < domains.length; i++) {
|
|
2192
|
+
for (let j = i + 1; j < domains.length; j++) {
|
|
2193
|
+
const a = domains[i];
|
|
2194
|
+
const b = domains[j];
|
|
2195
|
+
if (a.lo < b.hi && b.lo < a.hi) {
|
|
2196
|
+
errors.push(`Eras "${a.era.name}" and "${b.era.name}" overlap`);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
return errors;
|
|
2201
|
+
}
|
|
2202
|
+
function readCalendarConfig(value) {
|
|
2203
|
+
if (typeof value !== "object" || value === null) return null;
|
|
2204
|
+
const cfg = value;
|
|
2205
|
+
if (cfg.version !== 1 || typeof cfg.grain !== "string") return null;
|
|
2206
|
+
const months = cfg.bundles?.months;
|
|
2207
|
+
if (months && (!Array.isArray(months.names) || !Array.isArray(months.lengths) || months.names.length !== months.lengths.length || months.names.length === 0)) return null;
|
|
2208
|
+
if (cfg.eras && !Array.isArray(cfg.eras)) return null;
|
|
2209
|
+
if (cfg.eras?.some((e) => typeof e?.name !== "string" || !Number.isFinite(e?.start))) return null;
|
|
2210
|
+
if (cfg.intercalary && (!Array.isArray(cfg.intercalary) || cfg.intercalary.some((i) => typeof i?.name !== "string" || !Number.isInteger(i?.after_month)))) return null;
|
|
2211
|
+
if (months && !months.lengths.every(isPositiveInt)) return null;
|
|
2212
|
+
const year = cfg.bundles?.year;
|
|
2213
|
+
if (year != null && !isPositiveInt(year.steps)) return null;
|
|
2214
|
+
const week = cfg.bundles?.week;
|
|
2215
|
+
if (week != null && (!isPositiveInt(week.length) || !Array.isArray(week.names))) return null;
|
|
2216
|
+
if (cfg.intercalary?.some((i) => i.every_years != null && !isPositiveInt(i.every_years))) return null;
|
|
2217
|
+
return cfg;
|
|
2218
|
+
}
|
|
2219
|
+
function isPositiveInt(n) {
|
|
2220
|
+
return Number.isSafeInteger(n) && n >= 1;
|
|
2221
|
+
}
|
|
2222
|
+
function yearShape(cfg) {
|
|
2223
|
+
const months = cfg.bundles?.months;
|
|
2224
|
+
const inter = cfg.intercalary ?? [];
|
|
2225
|
+
const leapPeriods = inter.filter((i) => i.every_years != null).map((i) => i.every_years);
|
|
2226
|
+
if (months) {
|
|
2227
|
+
const base = months.lengths.reduce((a, b) => a + b, 0) + inter.filter((i) => i.every_years == null).length;
|
|
2228
|
+
return { baseLen: base, leapPeriods };
|
|
2229
|
+
}
|
|
2230
|
+
return { baseLen: cfg.bundles?.year?.steps ?? 1, leapPeriods };
|
|
2231
|
+
}
|
|
2232
|
+
function leapYearsBefore(y, N) {
|
|
2233
|
+
if (y > 0) return floorDiv(y - 1, N) + 1;
|
|
2234
|
+
if (y === 0) return 0;
|
|
2235
|
+
return -floorDiv(-y, N);
|
|
2236
|
+
}
|
|
2237
|
+
function isLeapYear(y, N) {
|
|
2238
|
+
return floorMod(y, N) === 0;
|
|
2239
|
+
}
|
|
2240
|
+
function yearStartStep(cfg, y) {
|
|
2241
|
+
const shape = yearShape(cfg);
|
|
2242
|
+
let start = y * shape.baseLen;
|
|
2243
|
+
for (const N of shape.leapPeriods) start += leapYearsBefore(y, N);
|
|
2244
|
+
return start;
|
|
2245
|
+
}
|
|
2246
|
+
function yearLength(cfg, y) {
|
|
2247
|
+
const shape = yearShape(cfg);
|
|
2248
|
+
let len = shape.baseLen;
|
|
2249
|
+
for (const N of shape.leapPeriods) if (isLeapYear(y, N)) len += 1;
|
|
2250
|
+
return len;
|
|
2251
|
+
}
|
|
2252
|
+
var MAX_YEAR_CORRECTION = 1e4;
|
|
2253
|
+
function absoluteYear(cfg, step) {
|
|
2254
|
+
const shape = yearShape(cfg);
|
|
2255
|
+
const meanLen = shape.leapPeriods.reduce((a, N) => a + 1 / N, shape.baseLen);
|
|
2256
|
+
let y = floorDiv(step, meanLen);
|
|
2257
|
+
if (!Number.isFinite(y)) return y;
|
|
2258
|
+
for (let i = 0; i < MAX_YEAR_CORRECTION && step < yearStartStep(cfg, y); i++) y -= 1;
|
|
2259
|
+
for (let i = 0; i < MAX_YEAR_CORRECTION && step >= yearStartStep(cfg, y + 1); i++) y += 1;
|
|
2260
|
+
return y;
|
|
2261
|
+
}
|
|
2262
|
+
function resolveEraDomains(eras) {
|
|
2263
|
+
const sorted = [...eras].sort((a, b) => a.start - b.start);
|
|
2264
|
+
const out = [];
|
|
2265
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
2266
|
+
const era = sorted[i];
|
|
2267
|
+
if ((era.direction ?? "up") === "down") {
|
|
2268
|
+
out.push({ era, lo: era.end ?? -Infinity, hi: era.start });
|
|
2269
|
+
} else {
|
|
2270
|
+
const nextStart = sorted[i + 1]?.start;
|
|
2271
|
+
out.push({ era, lo: era.start, hi: era.end ?? nextStart ?? Infinity });
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
return out;
|
|
2275
|
+
}
|
|
2276
|
+
function eraForStep(cfg, step) {
|
|
2277
|
+
const eras = cfg.eras ?? [];
|
|
2278
|
+
if (eras.length === 0) return null;
|
|
2279
|
+
const holding = resolveEraDomains(eras).filter((d) => step >= d.lo && step < d.hi);
|
|
2280
|
+
if (holding.length === 0) return null;
|
|
2281
|
+
holding.sort((a, b) => b.era.start - a.era.start || ((a.era.direction ?? "up") === "up" ? -1 : 1));
|
|
2282
|
+
return holding[0].era;
|
|
2283
|
+
}
|
|
2284
|
+
function displayYear(cfg, step, era) {
|
|
2285
|
+
const abs = absoluteYear(cfg, step);
|
|
2286
|
+
if (!era || era.restart === false) return abs;
|
|
2287
|
+
const base = era.year_base ?? 1;
|
|
2288
|
+
if ((era.direction ?? "up") === "down") {
|
|
2289
|
+
return absoluteYear(cfg, era.start) - abs + base - 1;
|
|
2290
|
+
}
|
|
2291
|
+
return abs - absoluteYear(cfg, era.start) + base;
|
|
2292
|
+
}
|
|
2293
|
+
function yearSlots(cfg, y) {
|
|
2294
|
+
const months = cfg.bundles?.months;
|
|
2295
|
+
if (!months) return [];
|
|
2296
|
+
const inter = cfg.intercalary ?? [];
|
|
2297
|
+
const active = (ic) => ic.every_years == null || isLeapYear(y, ic.every_years);
|
|
2298
|
+
const slots = [];
|
|
2299
|
+
const push = (afterMonth) => {
|
|
2300
|
+
for (const ic of inter) if (ic.after_month === afterMonth && active(ic)) slots.push({ kind: "intercalary", intercalary: ic });
|
|
2301
|
+
};
|
|
2302
|
+
push(0);
|
|
2303
|
+
for (let m = 1; m <= months.names.length; m++) {
|
|
2304
|
+
for (let d = 1; d <= months.lengths[m - 1]; d++) slots.push({ kind: "month", month: m, day: d });
|
|
2305
|
+
push(m);
|
|
2306
|
+
}
|
|
2307
|
+
return slots;
|
|
2308
|
+
}
|
|
2309
|
+
function skippedWeekDaysPerYear(cfg) {
|
|
2310
|
+
const inter = (cfg.intercalary ?? []).filter((i) => i.in_week === false);
|
|
2311
|
+
return {
|
|
2312
|
+
base: inter.filter((i) => i.every_years == null).length,
|
|
2313
|
+
leapPeriods: inter.filter((i) => i.every_years != null).map((i) => i.every_years)
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
function weekdayForStep(cfg, step, pre) {
|
|
2317
|
+
const week = cfg.bundles?.week;
|
|
2318
|
+
if (!week || week.length < 1 || week.names.length !== week.length) return null;
|
|
2319
|
+
const pos = pre ?? positionOf(cfg, step);
|
|
2320
|
+
if ((week.mode ?? "free") === "nested") {
|
|
2321
|
+
if (pos.slot?.kind !== "month") return null;
|
|
2322
|
+
return week.names[floorMod(pos.slot.day - 1, week.length)];
|
|
2323
|
+
}
|
|
2324
|
+
if (pos.slot?.kind === "intercalary" && pos.slot.intercalary?.in_week === false) return null;
|
|
2325
|
+
const skipped = skippedWeekDaysPerYear(cfg);
|
|
2326
|
+
const y = pos.absYear;
|
|
2327
|
+
let skippedBeforeYear = y * skipped.base;
|
|
2328
|
+
for (const N of skipped.leapPeriods) skippedBeforeYear += leapYearsBefore(y, N);
|
|
2329
|
+
let skippedInYear = 0;
|
|
2330
|
+
if (pos.slots) {
|
|
2331
|
+
for (let i = 0; i < pos.offset; i++) {
|
|
2332
|
+
const s = pos.slots[i];
|
|
2333
|
+
if (s.kind === "intercalary" && s.intercalary?.in_week === false) skippedInYear += 1;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
const counted = step - skippedBeforeYear - skippedInYear;
|
|
2337
|
+
return week.names[floorMod(counted, week.length)];
|
|
2338
|
+
}
|
|
2339
|
+
function positionOf(cfg, step) {
|
|
2340
|
+
const y = absoluteYear(cfg, step);
|
|
2341
|
+
const offset = step - yearStartStep(cfg, y);
|
|
2342
|
+
if (!cfg.bundles?.months) return { absYear: y, offset, slot: null, slots: null };
|
|
2343
|
+
const slots = yearSlots(cfg, y);
|
|
2344
|
+
return { absYear: y, offset, slot: slots[offset] ?? null, slots };
|
|
2345
|
+
}
|
|
2346
|
+
function breakdownStep(cfg, step) {
|
|
2347
|
+
const pos = positionOf(cfg, step);
|
|
2348
|
+
const era = eraForStep(cfg, step);
|
|
2349
|
+
const months = cfg.bundles?.months;
|
|
2350
|
+
const slot = pos.slot;
|
|
2351
|
+
return {
|
|
2352
|
+
step,
|
|
2353
|
+
absYear: pos.absYear,
|
|
2354
|
+
year: displayYear(cfg, step, era),
|
|
2355
|
+
era,
|
|
2356
|
+
month: slot?.kind === "month" ? slot.month : null,
|
|
2357
|
+
monthName: slot?.kind === "month" && months ? months.names[slot.month - 1] : null,
|
|
2358
|
+
day: slot?.kind === "month" ? slot.day : null,
|
|
2359
|
+
intercalary: slot?.kind === "intercalary" ? slot.intercalary.name : null,
|
|
2360
|
+
weekday: weekdayForStep(cfg, step, pos)
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
var KNOWN_TOKENS = ["day", "month", "weekday", "year", "era", "step", "intercalary"];
|
|
2364
|
+
var DEFAULT_FORMAT_MONTHS = "{day} {month}, {year} {era}";
|
|
2365
|
+
var DEFAULT_FORMAT_PLAIN = "{year} {era}";
|
|
2366
|
+
var DEFAULT_FORMAT_INTERCALARY = "{intercalary}, {year} {era}";
|
|
2367
|
+
var EMPTY_KNOWN = "\uE000";
|
|
2368
|
+
function renderTemplate(template, values) {
|
|
2369
|
+
let out = template.replace(/\{([a-z_]+)\}/g, (whole, token) => {
|
|
2370
|
+
if (!KNOWN_TOKENS.includes(token)) return whole;
|
|
2371
|
+
const v = values[token];
|
|
2372
|
+
return v == null || v === "" ? EMPTY_KNOWN : v;
|
|
2373
|
+
});
|
|
2374
|
+
out = out.replace(/\s*[,;·]?\s*\uE000\s*[,;·]?\s*/g, " ").replace(/\s{2,}/g, " ").trim().replace(/^[\s,;·]+|[\s,;·]+$/g, "");
|
|
2375
|
+
return out;
|
|
2376
|
+
}
|
|
2377
|
+
function formatStep(cfg, step) {
|
|
2378
|
+
const b = breakdownStep(cfg, step);
|
|
2379
|
+
const values = {
|
|
2380
|
+
day: b.day != null ? String(b.day) : null,
|
|
2381
|
+
month: b.monthName,
|
|
2382
|
+
weekday: b.weekday,
|
|
2383
|
+
year: String(b.year),
|
|
2384
|
+
era: b.era ? b.era.abbrev ?? b.era.name : null,
|
|
2385
|
+
step: String(step),
|
|
2386
|
+
intercalary: b.intercalary
|
|
2387
|
+
};
|
|
2388
|
+
const template = b.intercalary != null ? cfg.format_intercalary ?? DEFAULT_FORMAT_INTERCALARY : cfg.format ?? (cfg.bundles?.months ? DEFAULT_FORMAT_MONTHS : DEFAULT_FORMAT_PLAIN);
|
|
2389
|
+
return renderTemplate(template, values);
|
|
2390
|
+
}
|
|
2391
|
+
function formatYearLabel(cfg, step) {
|
|
2392
|
+
const era = eraForStep(cfg, step);
|
|
2393
|
+
const year = displayYear(cfg, step, era);
|
|
2394
|
+
const eraLabel = era ? era.abbrev ?? era.name : "";
|
|
2395
|
+
return eraLabel ? `${year} ${eraLabel}` : String(year);
|
|
2396
|
+
}
|
|
2397
|
+
function approxYearLength(cfg) {
|
|
2398
|
+
if (cfg.bundles?.months) {
|
|
2399
|
+
return cfg.bundles.months.lengths.reduce((a, b) => a + b, 0) + (cfg.intercalary ?? []).filter((i) => i.every_years == null).length;
|
|
2400
|
+
}
|
|
2401
|
+
return cfg.bundles?.year?.steps ?? null;
|
|
2402
|
+
}
|
|
2403
|
+
function formatDuration(cfg, steps) {
|
|
2404
|
+
const n = Math.abs(steps);
|
|
2405
|
+
const unit = (cfg.step_name ?? cfg.grain ?? "step").trim() || "step";
|
|
2406
|
+
const plural = (word, v) => v === 1 ? word : `${word}s`;
|
|
2407
|
+
const yearLen = cfg.bundles?.months ? cfg.bundles.months.lengths.reduce((a, b) => a + b, 0) + (cfg.intercalary ?? []).filter((i) => i.every_years == null).length : cfg.bundles?.year?.steps ?? (unit.toLowerCase() === "year" ? 1 : null);
|
|
2408
|
+
if (yearLen != null && yearLen > 1 && n >= yearLen) {
|
|
2409
|
+
const years = n / yearLen;
|
|
2410
|
+
const rounded = years >= 10 ? Math.round(years) : Math.round(years * 10) / 10;
|
|
2411
|
+
return `${rounded} ${plural("year", rounded)}`;
|
|
2412
|
+
}
|
|
2413
|
+
const shown = Math.round(n * 100) / 100;
|
|
2414
|
+
return `${shown} ${plural(unit, shown)}`;
|
|
2415
|
+
}
|
|
2416
|
+
function monthStartSteps(cfg, y) {
|
|
2417
|
+
const months = cfg.bundles?.months;
|
|
2418
|
+
if (!months) return [];
|
|
2419
|
+
const inter = cfg.intercalary ?? [];
|
|
2420
|
+
const active = (ic) => ic.every_years == null || isLeapYear(y, ic.every_years);
|
|
2421
|
+
const out = [];
|
|
2422
|
+
let offset = inter.filter((i) => i.after_month === 0 && active(i)).length;
|
|
2423
|
+
const start = yearStartStep(cfg, y);
|
|
2424
|
+
for (let m = 1; m <= months.names.length; m++) {
|
|
2425
|
+
out.push({ month: m, name: months.names[m - 1], step: start + offset });
|
|
2426
|
+
offset += months.lengths[m - 1] + inter.filter((i) => i.after_month === m && active(i)).length;
|
|
2427
|
+
}
|
|
2428
|
+
return out;
|
|
2429
|
+
}
|
|
2430
|
+
function formatMidLabel(cfg, step) {
|
|
2431
|
+
const b = breakdownStep(cfg, step);
|
|
2432
|
+
if (b.intercalary != null) return `${b.intercalary} ${b.year}`;
|
|
2433
|
+
if (b.monthName != null && b.day != null) return `${b.day} ${b.monthName} ${b.year}`;
|
|
2434
|
+
return formatYearLabel(cfg, step);
|
|
2435
|
+
}
|
|
2436
|
+
function absYearForEraYear(cfg, era, Y) {
|
|
2437
|
+
if (era.restart === false) return Y;
|
|
2438
|
+
const base = era.year_base ?? 1;
|
|
2439
|
+
if ((era.direction ?? "up") === "down") return absoluteYear(cfg, era.start) - (Y - base + 1);
|
|
2440
|
+
return absoluteYear(cfg, era.start) + (Y - base);
|
|
2441
|
+
}
|
|
2442
|
+
function defaultEra(cfg, now) {
|
|
2443
|
+
const eras = cfg.eras ?? [];
|
|
2444
|
+
if (eras.length === 0) return null;
|
|
2445
|
+
if (now != null) {
|
|
2446
|
+
const holder = eraForStep(cfg, now);
|
|
2447
|
+
if (holder) return holder;
|
|
2448
|
+
}
|
|
2449
|
+
const open = eras.filter((e) => !e.closed);
|
|
2450
|
+
const pool = open.length > 0 ? open : eras;
|
|
2451
|
+
return [...pool].sort((a, b) => b.start - a.start)[0];
|
|
2452
|
+
}
|
|
2453
|
+
function parseDate(cfg, input, opts = {}) {
|
|
2454
|
+
const raw = input.trim();
|
|
2455
|
+
if (raw === "") return { error: "empty date" };
|
|
2456
|
+
if (/^-?\d+$/.test(raw)) return { step: parseInt(raw, 10) };
|
|
2457
|
+
const normalize2 = (s) => ` ${s.toLowerCase().replace(/[,.]/g, " ").replace(/\s+/g, " ").trim()} `;
|
|
2458
|
+
const months = cfg.bundles?.months;
|
|
2459
|
+
const inter = cfg.intercalary ?? [];
|
|
2460
|
+
let era = null;
|
|
2461
|
+
let monthIndex = null;
|
|
2462
|
+
let intercalary = null;
|
|
2463
|
+
let rest = normalize2(raw).replace(/ year /g, " ");
|
|
2464
|
+
const eat = (name) => {
|
|
2465
|
+
const needle = normalize2(name);
|
|
2466
|
+
if (rest.includes(needle)) {
|
|
2467
|
+
rest = rest.replace(needle, " ");
|
|
2468
|
+
return true;
|
|
2469
|
+
}
|
|
2470
|
+
return false;
|
|
2471
|
+
};
|
|
2472
|
+
const eraCandidates = [...cfg.eras ?? []].flatMap((e) => [{ key: e.name, e }, ...e.abbrev ? [{ key: e.abbrev, e }] : []]).sort((a, b) => b.key.length - a.key.length);
|
|
2473
|
+
for (const c of eraCandidates) if (eat(c.key)) {
|
|
2474
|
+
era = c.e;
|
|
2475
|
+
break;
|
|
2476
|
+
}
|
|
2477
|
+
const interCandidates = [...inter].sort((a, b) => b.name.length - a.name.length);
|
|
2478
|
+
for (const c of interCandidates) if (eat(c.name)) {
|
|
2479
|
+
intercalary = c;
|
|
2480
|
+
break;
|
|
2481
|
+
}
|
|
2482
|
+
if (!intercalary && months) {
|
|
2483
|
+
for (let m = 1; m <= months.names.length; m++) {
|
|
2484
|
+
if (eat(months.names[m - 1])) {
|
|
2485
|
+
monthIndex = m;
|
|
2486
|
+
break;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
const numbers = [];
|
|
2491
|
+
for (const p of rest.trim().split(/\s+/).filter(Boolean)) {
|
|
2492
|
+
if (/^-?\d+$/.test(p)) numbers.push(parseInt(p, 10));
|
|
2493
|
+
else return { error: `unrecognized: "${p}"` };
|
|
2494
|
+
}
|
|
2495
|
+
let day = null;
|
|
2496
|
+
let year = null;
|
|
2497
|
+
if (monthIndex != null) {
|
|
2498
|
+
if (numbers.length === 2) {
|
|
2499
|
+
const len = months ? months.lengths[monthIndex - 1] : 0;
|
|
2500
|
+
const [a, b] = numbers;
|
|
2501
|
+
if (a >= 1 && a <= len) {
|
|
2502
|
+
day = a;
|
|
2503
|
+
year = b;
|
|
2504
|
+
} else if (b >= 1 && b <= len) {
|
|
2505
|
+
day = b;
|
|
2506
|
+
year = a;
|
|
2507
|
+
} else return { error: `no day in ${months?.names[monthIndex - 1]} matches ${a}/${b}` };
|
|
2508
|
+
} else if (numbers.length === 1) {
|
|
2509
|
+
const len = months ? months.lengths[monthIndex - 1] : 0;
|
|
2510
|
+
const n = numbers[0];
|
|
2511
|
+
if (n >= 1 && n <= len) {
|
|
2512
|
+
day = n;
|
|
2513
|
+
year = null;
|
|
2514
|
+
} else {
|
|
2515
|
+
year = n;
|
|
2516
|
+
}
|
|
2517
|
+
} else if (numbers.length !== 0) {
|
|
2518
|
+
return { error: "too many numbers" };
|
|
2519
|
+
}
|
|
2520
|
+
} else if (intercalary != null) {
|
|
2521
|
+
if (numbers.length > 1) return { error: "too many numbers" };
|
|
2522
|
+
year = numbers[0] ?? null;
|
|
2523
|
+
} else {
|
|
2524
|
+
if (numbers.length !== 1) return { error: "expected a year" };
|
|
2525
|
+
year = numbers[0];
|
|
2526
|
+
}
|
|
2527
|
+
const resolveInYear = (abs) => {
|
|
2528
|
+
const start = yearStartStep(cfg, abs);
|
|
2529
|
+
if (monthIndex == null && intercalary == null) return { step: start };
|
|
2530
|
+
const slots = yearSlots(cfg, abs);
|
|
2531
|
+
if (intercalary != null) {
|
|
2532
|
+
const idx2 = slots.findIndex((s) => s.kind === "intercalary" && s.intercalary === intercalary);
|
|
2533
|
+
if (idx2 === -1) return { error: `${intercalary.name} does not occur in that year` };
|
|
2534
|
+
return { step: start + idx2 };
|
|
2535
|
+
}
|
|
2536
|
+
const idx = slots.findIndex((s) => s.kind === "month" && s.month === monthIndex && s.day === (day ?? 1));
|
|
2537
|
+
if (idx === -1) return { error: "that day does not exist in that month" };
|
|
2538
|
+
return { step: start + idx };
|
|
2539
|
+
};
|
|
2540
|
+
if (year == null) {
|
|
2541
|
+
if (opts.now == null) return { error: "add a year (the world clock has no now set)" };
|
|
2542
|
+
return resolveInYear(absoluteYear(cfg, opts.now));
|
|
2543
|
+
}
|
|
2544
|
+
const target = era ?? defaultEra(cfg, opts.now);
|
|
2545
|
+
if (!target) return resolveInYear(year);
|
|
2546
|
+
const viaEra = resolveInYear(absYearForEraYear(cfg, target, year));
|
|
2547
|
+
if (isParseError(viaEra)) return viaEra;
|
|
2548
|
+
if (eraForStep(cfg, viaEra.step) === target) return viaEra;
|
|
2549
|
+
return resolveInYear(year);
|
|
2550
|
+
}
|
|
2551
|
+
function isParseError(r) {
|
|
2552
|
+
return r.error !== void 0;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
// src/calendar/index.ts
|
|
2556
|
+
var CALENDAR_SUPERTYPE = "calendar";
|
|
2557
|
+
var CALENDAR_FIELD = "atlas_calendar";
|
|
2558
|
+
function readCalendarValue(raw) {
|
|
2559
|
+
if (raw == null) return null;
|
|
2560
|
+
let value = raw;
|
|
2561
|
+
if (typeof raw === "string") {
|
|
2562
|
+
try {
|
|
2563
|
+
value = JSON.parse(raw);
|
|
2564
|
+
} catch {
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return readCalendarConfig(value);
|
|
2569
|
+
}
|
|
2570
|
+
function worldCalendars(elements) {
|
|
2571
|
+
const out = [];
|
|
2572
|
+
for (const el of elements) {
|
|
2573
|
+
const config = readCalendarValue(el[CALENDAR_FIELD]);
|
|
2574
|
+
if (!config) continue;
|
|
2575
|
+
out.push({ id: el.id, name: el.name || "(unnamed calendar)", config });
|
|
2576
|
+
}
|
|
2577
|
+
return out;
|
|
2578
|
+
}
|
|
2579
|
+
function resolveCalendarLens(elements, activeId, opts) {
|
|
2580
|
+
if (activeId === void 0 && opts?.fallbackToOnly) {
|
|
2581
|
+
const all = worldCalendars(elements);
|
|
2582
|
+
return all.length === 1 ? all[0] : null;
|
|
2583
|
+
}
|
|
2584
|
+
if (!activeId) return null;
|
|
2585
|
+
return worldCalendars(elements).find((c) => c.id === activeId) ?? null;
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2035
2588
|
// src/token-resource.ts
|
|
2036
2589
|
var TokenResource = class {
|
|
2037
2590
|
constructor(client) {
|
|
@@ -2204,6 +2757,8 @@ var GameTier = /* @__PURE__ */ ((GameTier2) => {
|
|
|
2204
2757
|
})(GameTier || {});
|
|
2205
2758
|
export {
|
|
2206
2759
|
ASK_LIMITS,
|
|
2760
|
+
CALENDAR_FIELD,
|
|
2761
|
+
CALENDAR_SUPERTYPE,
|
|
2207
2762
|
ELEMENT_FAMILIES,
|
|
2208
2763
|
ELEMENT_ICONS,
|
|
2209
2764
|
ELEMENT_LABELS,
|
|
@@ -2225,22 +2780,47 @@ export {
|
|
|
2225
2780
|
TokenResource,
|
|
2226
2781
|
WORLD_FILE_MAX_BYTES,
|
|
2227
2782
|
WORLD_FILE_MAX_ENTRIES,
|
|
2783
|
+
absYearForEraYear,
|
|
2784
|
+
absoluteYear,
|
|
2785
|
+
approxYearLength,
|
|
2786
|
+
breakdownStep,
|
|
2228
2787
|
buildAskContext,
|
|
2229
2788
|
buildWorldIndex,
|
|
2230
2789
|
detectKeyKind,
|
|
2790
|
+
displayYear,
|
|
2231
2791
|
elementColor,
|
|
2792
|
+
eraForStep,
|
|
2232
2793
|
errorFromResponse,
|
|
2233
2794
|
familyOf,
|
|
2795
|
+
floorDiv,
|
|
2796
|
+
floorMod,
|
|
2797
|
+
formatDuration,
|
|
2798
|
+
formatMidLabel,
|
|
2799
|
+
formatStep,
|
|
2800
|
+
formatYearLabel,
|
|
2234
2801
|
getElementIcon,
|
|
2235
2802
|
getElementLabel,
|
|
2236
2803
|
isDemoKey,
|
|
2804
|
+
isLeapYear,
|
|
2805
|
+
isParseError,
|
|
2237
2806
|
kindCanWrite,
|
|
2238
2807
|
lookupWorld,
|
|
2808
|
+
monthStartSteps,
|
|
2809
|
+
parseDate,
|
|
2239
2810
|
parseErrorEnvelope,
|
|
2240
2811
|
pinExpectation,
|
|
2241
2812
|
planPhoto,
|
|
2242
2813
|
preparePhoto,
|
|
2814
|
+
readCalendarConfig,
|
|
2815
|
+
readCalendarValue,
|
|
2243
2816
|
readWorldFile,
|
|
2244
2817
|
renderElement,
|
|
2245
|
-
|
|
2818
|
+
resolveCalendarLens,
|
|
2819
|
+
resolveEraDomains,
|
|
2820
|
+
validateCalendarConfig,
|
|
2821
|
+
weekdayForStep,
|
|
2822
|
+
worldCalendars,
|
|
2823
|
+
writeWorldFile,
|
|
2824
|
+
yearLength,
|
|
2825
|
+
yearStartStep
|
|
2246
2826
|
};
|