temporal-fmt 0.8.1 → 0.8.2

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.
@@ -0,0 +1,2 @@
1
+ export declare const MAX_FORMAT_LENGTH = 1000;
2
+ export declare const MAX_INPUT_LENGTH = 100000;
@@ -0,0 +1,13 @@
1
+ import { type TemporalLike, type FormatOptions } from './tokens.cjs';
2
+ /**
3
+ * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime
4
+ * using a date-fns-style token string.
5
+ *
6
+ * @example
7
+ * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // "2026-08-04"
8
+ * format(zdt, "MMM d, yyyy 'at' h:mm a") // "Aug 4, 2026 at 3:45 PM"
9
+ * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // "août 4, 2026"
10
+ *
11
+ * Throws on a token the input type doesn't support (e.g. 'HH' on a PlainDate).
12
+ */
13
+ export declare function format(temporal: TemporalLike, formatStr: string, options?: FormatOptions): string;
@@ -0,0 +1,5 @@
1
+ export { format } from './format.cjs';
2
+ export { parse } from './parse.cjs';
3
+ export { setTemporal } from './temporalProvider.cjs';
4
+ export type { TemporalLike, FormatOptions } from './tokens.cjs';
5
+ export type { TemporalNamespace } from './temporalProvider.cjs';
@@ -0,0 +1,9 @@
1
+ export interface LocaleVocab {
2
+ monthLong: string[];
3
+ monthShort: string[];
4
+ weekdayLong: string[];
5
+ weekdayShort: string[];
6
+ dayPeriod: string[];
7
+ }
8
+ export declare function canonicalCacheKey(locale: string): string;
9
+ export declare function getLocaleVocab(locale: string): LocaleVocab;
@@ -0,0 +1,24 @@
1
+ import { type FormatOptions } from './tokens.cjs';
2
+ /**
3
+ * Parses `input` against `formatStr` and builds the real Temporal value it
4
+ * describes: a `Temporal.PlainDate`, `PlainTime`, `PlainDateTime`, or
5
+ * `ZonedDateTime` depending on which tokens are present.
6
+ *
7
+ * Returns `unknown` — this package has no ambient `Temporal` types to return
8
+ * a real one against.
9
+ *
10
+ * `options.locale` picks the calendar the result is built in. Pass a locale
11
+ * tag with a `-u-ca-` extension (e.g. `'en-u-ca-hebrew'`) to parse into a
12
+ * non-Gregorian calendar.
13
+ *
14
+ * @throws if `input` doesn't match `formatStr`'s shape at all
15
+ * @throws if it matches the shape but describes an impossible date (e.g. Feb
16
+ * 30) or self-contradictory data (e.g. a weekday name that doesn't match the
17
+ * actual date)
18
+ *
19
+ * @example
20
+ * parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // Temporal.PlainDateTime
21
+ * parse('yyyy-MM', '2026-08-04T15:45:30') // throws — shape doesn't match
22
+ * parse('yyyy-MM-dd', '2026-02-30') // throws — not a real date
23
+ */
24
+ export declare function parse(formatStr: string, input: string, options?: FormatOptions): unknown | undefined;
@@ -0,0 +1,19 @@
1
+ import type { Piece } from './tokenize.cjs';
2
+ export interface CapturingPattern {
3
+ regex: RegExp;
4
+ groups: Array<{
5
+ name: string;
6
+ token: string;
7
+ }>;
8
+ ambiguousRuns: Array<{
9
+ groupNames: string[];
10
+ tokens: string[];
11
+ }>;
12
+ }
13
+ /**
14
+ * Same walk as buildPatternSource() in pattern.ts, but each token piece
15
+ * gets its own named capture group (positionally named so the same token,
16
+ * e.g. "yyyy", could in theory appear twice) so a caller can pull the
17
+ * matched substring for each token back out after a successful match.
18
+ */
19
+ export declare function buildCapturingPattern(pieces: Piece[], locale: string): CapturingPattern;
@@ -0,0 +1,28 @@
1
+ export declare function isValidTimeZone(raw: string): boolean;
2
+ export declare function tokenFragment(token: string, locale: string, nextToken?: string): string;
3
+ export declare const UNPADDED_NUMERIC_TOKENS: Set<string>;
4
+ export declare const UNPADDED_NUMERIC_RANGES: Record<string, Array<{
5
+ digits: 1 | 2;
6
+ min: number;
7
+ max: number;
8
+ }>>;
9
+ /**
10
+ * Given the literal digit string a run of N adjacent unpadded-numeric
11
+ * tokens matched as a whole (e.g. "112" for a 2-token run), enumerates
12
+ * every way to split it into N pieces (one per token, each piece 1-2
13
+ * digits per that token's own width rule) and returns every split where
14
+ * every piece is independently valid for its token. Length 0 means the
15
+ * run's regex match shouldn't have been possible in the first place
16
+ * (shouldn't happen — the caller only invokes this after the whole
17
+ * pattern already matched, meaning at least one split exists: the one the
18
+ * regex actually took). Length 1 means the reading is unambiguous.
19
+ * Length 2+ means true ambiguity — the caller should throw rather than
20
+ * pick one.
21
+ *
22
+ * Recursive over token count rather than hardcoded to 2, so a 3+ token
23
+ * unseparated run (e.g. "Hms") is covered by the same logic without a
24
+ * special case — those are rarer in practice but not impossible, and a
25
+ * partial fix that only covered pairs would leave the identical bug for
26
+ * anyone writing a 3-token glued run.
27
+ */
28
+ export declare function enumerateValidSplits(digits: string, tokens: string[]): number[][];
@@ -0,0 +1,28 @@
1
+ interface TemporalFactory {
2
+ from(fields: Record<string, number | string | undefined>, options?: {
3
+ overflow?: 'constrain' | 'reject';
4
+ }): unknown;
5
+ }
6
+ export interface TemporalNamespace {
7
+ PlainDate: TemporalFactory;
8
+ PlainTime: TemporalFactory;
9
+ PlainDateTime: TemporalFactory;
10
+ ZonedDateTime: TemporalFactory;
11
+ }
12
+ export declare function subscribeToTemporalChanges(listener: () => void): void;
13
+ /**
14
+ * Explicitly hand temporal-fmt the Temporal implementation to use, instead
15
+ * of relying on a global `Temporal`. Call this once, before your first
16
+ * `format()`/`parse()`
17
+ *
18
+ * Call with no argument (or `undefined`) to clear the override and fall
19
+ * back to `globalThis.Temporal` again.
20
+ *
21
+ * @example
22
+ * import { Temporal } from 'temporal-polyfill';
23
+ * import { setTemporal } from 'temporal-fmt';
24
+ * setTemporal(Temporal);
25
+ */
26
+ export declare function setTemporal(temporal?: TemporalNamespace): void;
27
+ export declare function getTemporal(): TemporalNamespace;
28
+ export {};
@@ -0,0 +1,14 @@
1
+ export type Piece = {
2
+ kind: 'token';
3
+ value: string;
4
+ } | {
5
+ kind: 'literal';
6
+ value: string;
7
+ };
8
+ /**
9
+ * Splits a format string like `"yyyy-MM-dd 'at' HH:mm"` into token/literal
10
+ * pieces. Text in single quotes is always literal (e.g. write 'rd' in
11
+ * "3rd" so it's not read as the day token). A doubled quote ('') means a
12
+ * literal quote character, both inside a quoted span and standalone.
13
+ */
14
+ export declare function tokenize(format: string): Piece[];
@@ -0,0 +1,23 @@
1
+ export declare function pad(n: number, len: number): string;
2
+ export interface TemporalLike {
3
+ year?: number;
4
+ month?: number;
5
+ day?: number;
6
+ hour?: number;
7
+ minute?: number;
8
+ second?: number;
9
+ millisecond?: number;
10
+ timeZoneId?: string;
11
+ dayOfWeek?: number;
12
+ calendarId?: string;
13
+ toInstant?: () => unknown;
14
+ toLocaleString?: (locale: string, options: Intl.DateTimeFormatOptions) => string;
15
+ }
16
+ export interface FormatOptions {
17
+ /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */
18
+ locale?: string;
19
+ }
20
+ export declare const DEFAULT_LOCALE = "en-US";
21
+ type TokenHandler = (t: TemporalLike, locale: string) => string;
22
+ export declare const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]>;
23
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temporal-fmt",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Format Temporal.PlainDate/PlainDateTime/PlainTime/ZonedDateTime objects using date-fns-style token strings.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -8,9 +8,14 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
14
19
  }
15
20
  },
16
21
  "files": [
@@ -18,13 +23,14 @@
18
23
  ],
19
24
  "sideEffects": false,
20
25
  "scripts": {
21
- "build": "tsup && tsc --declaration --emitDeclarationOnly",
26
+ "build": "tsup && tsc --declaration --emitDeclarationOnly && node scripts/emit-cjs-types.mjs",
22
27
  "dev": "tsup --watch",
23
- "test": "node --test test/*.test.js",
28
+ "test": "node --test test/*.test.js test/*.test.cjs",
24
29
  "test:unit": "vitest run",
25
30
  "test:unit:watch": "vitest",
26
31
  "test:types": "vitest run --typecheck",
27
- "test:all": "npm run build && npm test && npm run test:unit && npm run test:types",
32
+ "test:pack": "attw --pack .",
33
+ "test:all": "npm run build && npm test && npm run test:unit && npm run test:types && npm run test:pack",
28
34
  "prepublishOnly": "npm run build && npm test"
29
35
  },
30
36
  "keywords": [
@@ -48,6 +54,7 @@
48
54
  },
49
55
  "homepage": "https://github.com/DirazCoder/temporal-fmt#readme",
50
56
  "devDependencies": {
57
+ "@arethetypeswrong/cli": "^0.18.5",
51
58
  "temporal-polyfill": "^1.0.4",
52
59
  "tsup": "^8.5.1",
53
60
  "typescript": "^7.0.2",