@samva/markup 0.8.0 → 0.10.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 +107 -46
- package/CHANGELOG.md +345 -0
- package/README.md +188 -52
- package/dist/{brand-DcoVkh_S.d.ts → assets-BIsyMQ92.d.ts} +41 -11
- package/dist/{email/brand.js → brand-C0MKTwmX.js} +64 -4
- package/dist/{theme-css.d.ts → brand.d.ts} +12 -3
- package/dist/brand.js +4 -0
- package/dist/channel-specs-C2feJwuP.d.ts +13 -0
- package/dist/compiler.d.ts +88 -115
- package/dist/compiler.js +750 -171
- package/dist/components-BZCqegS8.js +1864 -0
- package/dist/diagnostic-model-DJtqbETd.js +38 -0
- package/dist/{diagnostics-OcQKQYSY.d.ts → diagnostic-model-DxthUZBT.d.ts} +3 -21
- package/dist/{diagnostics-BMYtBCQk.d.ts → diagnostics-B-Gym34R.d.ts} +8 -4
- package/dist/{email/diagnostics.js → diagnostics-CnKFefKz.js} +4 -3
- package/dist/diagnostics.d.ts +410 -2
- package/dist/diagnostics.js +402 -52
- package/dist/edit.d.ts +234 -0
- package/dist/edit.js +947 -0
- package/dist/email/jsx-dev-runtime.d.ts +2 -2
- package/dist/email/jsx-dev-runtime.js +1 -2
- package/dist/email/jsx-runtime.d.ts +1 -1
- package/dist/email/jsx-runtime.js +1 -85
- package/dist/{email/components.d.ts → email.d.ts} +22 -11
- package/dist/email.js +2 -0
- package/dist/fmt.d.ts +32 -0
- package/dist/fmt.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/{render-vAv_4r9v.js → ir-CLhlklKV.js} +96 -279
- package/dist/jsx-runtime-CBlexoeR.js +98 -0
- package/dist/{elements-BzjjnoCS.d.ts → jsx-runtime-ylIOnaSf.d.ts} +76 -76
- package/dist/profile-pWGtWZXF.js +3803 -0
- package/dist/render-BPonDpCl.d.ts +539 -0
- package/dist/render-BQmfh94k.js +257 -0
- package/dist/render.d.ts +35 -0
- package/dist/render.js +49 -0
- package/dist/sms.d.ts +16 -0
- package/dist/sms.js +12 -0
- package/dist/source-locations-C8wCsiAy.d.ts +23 -0
- package/dist/template-DMPwxMSq.d.ts +65 -0
- package/dist/template-zLUjcwfF.js +42 -0
- package/dist/{theme-css-hFqcPSar.js → theme-css-JtRCm1fb.js} +2 -2
- package/dist/whatsapp.d.ts +57 -0
- package/dist/whatsapp.js +22 -0
- package/generated/contract.json +1788 -0
- package/generated/reference.md +360 -0
- package/package.json +46 -239
- package/samva-brand.d.ts +4 -4
- package/skills/sml/SKILL.md +123 -0
- package/dist/ast-BrDtApwS.d.ts +0 -107
- package/dist/ast.d.ts +0 -2
- package/dist/ast.js +0 -124
- package/dist/candidate.d.ts +0 -29
- package/dist/candidate.js +0 -88
- package/dist/catalog-CKuOooB8.d.ts +0 -354
- package/dist/checks-D73Xizbw.js +0 -59366
- package/dist/checks.d.ts +0 -33
- package/dist/checks.js +0 -2
- package/dist/classes.d.ts +0 -26
- package/dist/classes.js +0 -184
- package/dist/compile-sms.d.ts +0 -28
- package/dist/compile-sms.js +0 -44
- package/dist/compile-whatsapp.d.ts +0 -56
- package/dist/compile-whatsapp.js +0 -186
- package/dist/compile.d.ts +0 -18
- package/dist/compile.js +0 -29
- package/dist/components.d.ts +0 -289
- package/dist/components.js +0 -106
- package/dist/conditionals.d.ts +0 -10
- package/dist/conditionals.js +0 -74
- package/dist/corpus-CPPfgAHN.d.ts +0 -106
- package/dist/css-CS2jLID6.d.ts +0 -109
- package/dist/email/assets.d.ts +0 -40
- package/dist/email/assets.js +0 -63
- package/dist/email/brand-footer.d.ts +0 -34
- package/dist/email/brand-footer.js +0 -62
- package/dist/email/brand.d.ts +0 -2
- package/dist/email/components.js +0 -240
- package/dist/email/diagnostics.d.ts +0 -2
- package/dist/email/elements.d.ts +0 -2
- package/dist/email/elements.js +0 -299
- package/dist/email/html.d.ts +0 -2
- package/dist/email/html.js +0 -278
- package/dist/email/render.d.ts +0 -50
- package/dist/email/render.js +0 -3
- package/dist/email/selection.d.ts +0 -2
- package/dist/email/selection.js +0 -91
- package/dist/email/source-edit.d.ts +0 -67
- package/dist/email/source-edit.js +0 -185
- package/dist/email/stylesheets.d.ts +0 -15
- package/dist/email/stylesheets.js +0 -48
- package/dist/email/text.d.ts +0 -7
- package/dist/email/text.js +0 -100
- package/dist/emit.d.ts +0 -51
- package/dist/emit.js +0 -151
- package/dist/html-Bl9BAdqc.d.ts +0 -42
- package/dist/interpolate.d.ts +0 -24
- package/dist/interpolate.js +0 -43
- package/dist/jsx-dev-runtime.d.ts +0 -11
- package/dist/jsx-dev-runtime.js +0 -15
- package/dist/jsx-runtime-I-1nCtl2.d.ts +0 -57
- package/dist/jsx-runtime.d.ts +0 -2
- package/dist/jsx-runtime.js +0 -121
- package/dist/lexer.d.ts +0 -60
- package/dist/lexer.js +0 -281
- package/dist/model.d.ts +0 -24
- package/dist/model.js +0 -77
- package/dist/ops-BvwqHl7V.js +0 -630
- package/dist/ops.d.ts +0 -114
- package/dist/ops.js +0 -2
- package/dist/parser-t29cHLVL.d.ts +0 -396
- package/dist/parser.d.ts +0 -2
- package/dist/parser.js +0 -859
- package/dist/predicate-BI6uRRM-.js +0 -549
- package/dist/predicate-M-05Nd7R.d.ts +0 -56
- package/dist/predicate.d.ts +0 -2
- package/dist/predicate.js +0 -2
- package/dist/printer.d.ts +0 -6
- package/dist/printer.js +0 -122
- package/dist/registry-Dcp6QEK_.d.ts +0 -73
- package/dist/registry.d.ts +0 -2
- package/dist/registry.js +0 -646
- package/dist/render-template.d.ts +0 -45
- package/dist/render-template.js +0 -76
- package/dist/selection-CCArx9lU.d.ts +0 -91
- package/dist/showcase-egkusQBf.d.ts +0 -415
- package/dist/sms-segments-y4FpPilJ.d.ts +0 -13
- package/dist/sms-segments.d.ts +0 -2
- package/dist/sms-segments.js +0 -46
- package/dist/source-locations-DrCM82Ym.d.ts +0 -67
- package/dist/source-locations.d.ts +0 -2
- package/dist/source-locations.js +0 -102
- package/dist/starters/category-mapping.d.ts +0 -13
- package/dist/starters/category-mapping.js +0 -49
- package/dist/starters/corpus.d.ts +0 -2
- package/dist/starters/corpus.js +0 -194
- package/dist/starters/library.d.ts +0 -18
- package/dist/starters/library.js +0 -692
- package/dist/starters/public-gallery.d.ts +0 -266
- package/dist/starters/public-gallery.js +0 -916
- package/dist/starters/public-handoff.d.ts +0 -164
- package/dist/starters/public-handoff.js +0 -361
- package/dist/starters/public-preview-artifacts.d.ts +0 -43
- package/dist/starters/public-preview-artifacts.js +0 -104
- package/dist/starters/public-preview.d.ts +0 -61
- package/dist/starters/public-preview.js +0 -83
- package/dist/starters/showcase.d.ts +0 -2
- package/dist/starters/showcase.js +0 -335
- package/dist/starters/sources.d.ts +0 -45
- package/dist/starters/sources.js +0 -7762
- package/dist/starters/theme.d.ts +0 -45
- package/dist/starters/theme.js +0 -77
- package/dist/starters.d.ts +0 -2
- package/dist/starters.js +0 -874
- package/dist/tailwind-BYq7Lo6u.js +0 -727
- package/dist/template-DWftqh4x.d.ts +0 -60
- package/dist/template.d.ts +0 -2
- package/dist/template.js +0 -37
- package/dist/theme-css.js +0 -2
- package/dist/tsx-source-instrumentation.d.ts +0 -30
- package/dist/tsx-source-instrumentation.js +0 -115
- package/dist/tsx-tracer.d.ts +0 -112
- package/dist/tsx-tracer.js +0 -985
- package/dist/variables-J_L3qxRa.d.ts +0 -77
- package/dist/variables.d.ts +0 -2
- package/dist/variables.js +0 -141
- package/dist/version-BITr58kk.js +0 -96
- package/generated/sml-agent-contract.json +0 -709
- package/generated/sml-email-agent-contract.json +0 -615
- package/generated/sml-email-language-reference.md +0 -94
- package/generated/sml-language-reference.md +0 -195
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as JsxSource } from "../source-locations-C8wCsiAy.js";
|
|
2
|
+
import { a as JSX, i as Fragment, n as EmailComponent, r as EmailNode } from "../jsx-runtime-ylIOnaSf.js";
|
|
3
3
|
//#region src/email/jsx-dev-runtime.d.ts
|
|
4
4
|
/** Development JSX retains compiler-provided origins directly on the semantic tree. */
|
|
5
5
|
declare const jsxDEV: (type: string | EmailComponent<Record<string, unknown>> | typeof Fragment, props: Record<string, unknown> | null, _key?: unknown, _staticChildren?: boolean, source?: JsxSource) => EmailNode;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { JSX_SOURCE_LOCATION_PROP } from "../
|
|
2
|
-
import { Fragment, jsx } from "./jsx-runtime.js";
|
|
1
|
+
import { a as jsx, n as Fragment, u as JSX_SOURCE_LOCATION_PROP } from "../jsx-runtime-CBlexoeR.js";
|
|
3
2
|
//#region src/email/jsx-dev-runtime.ts
|
|
4
3
|
/** Development JSX retains compiler-provided origins directly on the semantic tree. */
|
|
5
4
|
const jsxDEV = (type, props, _key, _staticChildren, source) => jsx(type, {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as JSX, c as jsx, d as rawMarkup, f as reportEmailDiagnostic, i as Fragment, l as jsxs, n as EmailComponent, o as currentEmailOrigins, r as EmailNode, s as drainEmailDiagnostics, t as EmailChild, u as normalizeChildren } from "../jsx-runtime-ylIOnaSf.js";
|
|
2
2
|
export { EmailChild, EmailComponent, EmailNode, Fragment, JSX, currentEmailOrigins, drainEmailDiagnostics, jsx, jsxs, normalizeChildren, rawMarkup, reportEmailDiagnostic };
|
|
@@ -1,86 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import { JSX_SOURCE_LOCATION_PROP, completeJsxSourceLocation } from "../source-locations.js";
|
|
3
|
-
//#region src/email/jsx-runtime.ts
|
|
4
|
-
/** Constructors for the authoring tree. The tree is data; nothing here validates it. */
|
|
5
|
-
const EmailNode = {
|
|
6
|
-
Text: (fields) => ({
|
|
7
|
-
type: "Text",
|
|
8
|
-
...fields
|
|
9
|
-
}),
|
|
10
|
-
Element: (fields) => ({
|
|
11
|
-
type: "Element",
|
|
12
|
-
...fields
|
|
13
|
-
}),
|
|
14
|
-
Fragment: (fields) => ({
|
|
15
|
-
type: "Fragment",
|
|
16
|
-
...fields
|
|
17
|
-
}),
|
|
18
|
-
Raw: (fields) => ({
|
|
19
|
-
type: "Raw",
|
|
20
|
-
...fields
|
|
21
|
-
})
|
|
22
|
-
};
|
|
23
|
-
const EMAIL_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
24
|
-
"Text",
|
|
25
|
-
"Element",
|
|
26
|
-
"Fragment",
|
|
27
|
-
"Raw"
|
|
28
|
-
]);
|
|
29
|
-
/** True for a node this runtime built; anything else is not a child it can place. */
|
|
30
|
-
const isEmailNode = (input) => hasProperty(input, "type") && typeof input.type === "string" && EMAIL_NODE_TYPES.has(input.type);
|
|
31
|
-
const Fragment = Symbol.for("@samva/markup/email/Fragment");
|
|
32
|
-
const callSites = [];
|
|
33
|
-
/** Call sites enclosing the element being built, innermost first. */
|
|
34
|
-
const currentEmailOrigins = () => [...callSites].reverse();
|
|
35
|
-
const reported = [];
|
|
36
|
-
/** Record a finding from inside a compiler primitive, located at the authoring call site. */
|
|
37
|
-
const reportEmailDiagnostic = (diagnostic) => {
|
|
38
|
-
reported.push({
|
|
39
|
-
...diagnostic,
|
|
40
|
-
origins: currentEmailOrigins()
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
/** Take and clear the findings primitives reported during a render. */
|
|
44
|
-
const drainEmailDiagnostics = () => reported.splice(0);
|
|
45
|
-
/** Normalize actual values without interpolation or recording proxies. */
|
|
46
|
-
const normalizeChildren = (input) => {
|
|
47
|
-
if (input === null || input === void 0 || typeof input === "boolean") return [];
|
|
48
|
-
if (typeof input === "string") return [EmailNode.Text({ value: input })];
|
|
49
|
-
if (typeof input === "number" && Number.isFinite(input)) return [EmailNode.Text({ value: String(input) })];
|
|
50
|
-
if (Array.isArray(input)) return input.flatMap(normalizeChildren);
|
|
51
|
-
if (isEmailNode(input)) return [input];
|
|
52
|
-
throw new TypeError("Email children must be synchronous text, finite numbers, elements, or arrays");
|
|
53
|
-
};
|
|
54
|
-
/** Compatibility markup emitted by a compiler primitive, with the text it stands for. */
|
|
55
|
-
const rawMarkup = (html, text = "") => EmailNode.Raw({
|
|
56
|
-
html,
|
|
57
|
-
text,
|
|
58
|
-
origins: currentEmailOrigins()
|
|
59
|
-
});
|
|
60
|
-
/** Automatic JSX factory for ordinary HTML and synchronous imported components. */
|
|
61
|
-
const jsx = (type, props) => {
|
|
62
|
-
const { children, [JSX_SOURCE_LOCATION_PROP]: source, ...attributes } = props ?? {};
|
|
63
|
-
const origin = completeJsxSourceLocation(source);
|
|
64
|
-
if (type === Fragment) return EmailNode.Fragment({ children: normalizeChildren(children) });
|
|
65
|
-
if (typeof type === "string") return EmailNode.Element({
|
|
66
|
-
tag: type,
|
|
67
|
-
props: attributes,
|
|
68
|
-
children: normalizeChildren(children),
|
|
69
|
-
origins: origin === void 0 ? currentEmailOrigins() : [origin, ...currentEmailOrigins()],
|
|
70
|
-
authored: origin !== void 0
|
|
71
|
-
});
|
|
72
|
-
if (origin !== void 0) callSites.push(origin);
|
|
73
|
-
try {
|
|
74
|
-
return EmailNode.Fragment({ children: normalizeChildren(type({
|
|
75
|
-
...attributes,
|
|
76
|
-
children
|
|
77
|
-
})) });
|
|
78
|
-
} finally {
|
|
79
|
-
if (origin !== void 0) callSites.pop();
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
const jsxs = jsx;
|
|
83
|
-
//#endregion
|
|
1
|
+
import { a as jsx, c as rawMarkup, i as drainEmailDiagnostics, l as reportEmailDiagnostic, n as Fragment, o as jsxs, r as currentEmailOrigins, s as normalizeChildren, t as EmailNode } from "../jsx-runtime-CBlexoeR.js";
|
|
84
2
|
export { EmailNode, Fragment, currentEmailOrigins, drainEmailDiagnostics, jsx, jsxs, normalizeChildren, rawMarkup, reportEmailDiagnostic };
|
|
85
|
-
|
|
86
|
-
//# sourceMappingURL=jsx-runtime.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as EmailStyle, r as EmailNode, t as EmailChild } from "./jsx-runtime-ylIOnaSf.js";
|
|
2
2
|
//#region src/email/components.d.ts
|
|
3
3
|
/** Marks the hidden preview block so the renderer can read it back and the text lane can keep it. */
|
|
4
4
|
declare const PREHEADER_ATTRIBUTE = "data-samva-preheader";
|
|
@@ -9,8 +9,6 @@ interface EmailProps {
|
|
|
9
9
|
readonly dir?: "ltr" | "rtl" | undefined;
|
|
10
10
|
/** Document title; several clients use it as the fallback preview line. */
|
|
11
11
|
readonly title?: string | undefined;
|
|
12
|
-
/** Hidden preview text shown in the inbox list next to the subject. */
|
|
13
|
-
readonly preheader?: string | undefined;
|
|
14
12
|
readonly backgroundColor?: string | undefined;
|
|
15
13
|
/** Applied to the content cell, where Yahoo and AOL keep body styling. */
|
|
16
14
|
readonly style?: EmailStyle | undefined;
|
|
@@ -19,7 +17,7 @@ interface EmailProps {
|
|
|
19
17
|
/** The hidden inbox preview block. Padding characters stop the client borrowing body copy. */
|
|
20
18
|
declare const preheaderNode: (text: string) => EmailNode;
|
|
21
19
|
/** Document shell: the head every target client needs, plus the body content cell. */
|
|
22
|
-
declare const Email: ({ className, lang, dir, title,
|
|
20
|
+
declare const Email: ({ className, lang, dir, title, backgroundColor, style, children }: EmailProps) => EmailNode;
|
|
23
21
|
interface SectionProps {
|
|
24
22
|
/** CSS classes applied to the primitive's content element. */
|
|
25
23
|
readonly className?: string | undefined;
|
|
@@ -33,17 +31,30 @@ interface SectionProps {
|
|
|
33
31
|
}
|
|
34
32
|
/** A full-width band of content, as the single-cell table every client agrees on. */
|
|
35
33
|
declare const Section: ({ className, align, width, bgcolor, style, tableStyle, children }: SectionProps) => EmailNode;
|
|
36
|
-
interface ColumnsProps {
|
|
34
|
+
interface ColumnsProps<Item = never> {
|
|
37
35
|
/** CSS classes applied to the primitive's content element. */
|
|
38
36
|
readonly className?: string | undefined;
|
|
39
37
|
readonly align?: "left" | "center" | "right" | undefined;
|
|
40
38
|
readonly width?: number | string | undefined;
|
|
41
39
|
readonly style?: EmailStyle | undefined;
|
|
42
|
-
/**
|
|
43
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Repeat the cells for each item: `children` is then a function from an item (and its index in
|
|
42
|
+
* the row) to a `Column`. Without `per` all items share one row.
|
|
43
|
+
*/
|
|
44
|
+
readonly each?: readonly Item[] | undefined;
|
|
45
|
+
/** With `each`, the cells in a row; each chunk of items is its own `Columns` table. */
|
|
46
|
+
readonly per?: number | undefined;
|
|
47
|
+
/** `Column` elements, or with `each` a function that returns one. */
|
|
48
|
+
readonly children?: EmailChild | ((item: Item, index: number) => EmailChild);
|
|
44
49
|
}
|
|
45
|
-
/**
|
|
46
|
-
declare const
|
|
50
|
+
/** The equal share of a row that each of `count` cells takes, whole percent rounded down. */
|
|
51
|
+
declare const equalColumnWidth: (count: number) => string;
|
|
52
|
+
/**
|
|
53
|
+
* A row of side-by-side `Column`s. No flex or grid conversion happens here. With `each` it repeats
|
|
54
|
+
* its cells over a list, `per` to a row, and splits the row equally between cells that state no
|
|
55
|
+
* width.
|
|
56
|
+
*/
|
|
57
|
+
declare const Columns: <Item = never>({ className, align, width, style, each, per, children }: ColumnsProps<Item>) => EmailNode;
|
|
47
58
|
interface ColumnProps {
|
|
48
59
|
/** CSS classes applied to the primitive's content element. */
|
|
49
60
|
readonly className?: string | undefined;
|
|
@@ -121,5 +132,5 @@ interface LinkProps {
|
|
|
121
132
|
/** A text link that opens outside the client and cannot reach back at the opener. */
|
|
122
133
|
declare const Link: ({ className, href, target, rel, style, children }: LinkProps) => EmailNode;
|
|
123
134
|
//#endregion
|
|
124
|
-
export { Button, ButtonProps, Column, ColumnProps, Columns, ColumnsProps, Divider, DividerProps, Email, EmailProps, Image, ImageProps, Link, LinkProps, PREHEADER_ATTRIBUTE, Section, SectionProps, Spacer, SpacerProps, preheaderNode };
|
|
125
|
-
//# sourceMappingURL=
|
|
135
|
+
export { Button, ButtonProps, Column, ColumnProps, Columns, ColumnsProps, Divider, DividerProps, Email, EmailProps, Image, ImageProps, Link, LinkProps, PREHEADER_ATTRIBUTE, Section, SectionProps, Spacer, SpacerProps, equalColumnWidth, preheaderNode };
|
|
136
|
+
//# sourceMappingURL=email.d.ts.map
|
package/dist/email.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as Email, c as PREHEADER_ATTRIBUTE, d as equalColumnWidth, f as preheaderNode, i as Divider, l as Section, n as Column, o as Image, r as Columns, s as Link, t as Button, u as Spacer } from "./components-BZCqegS8.js";
|
|
2
|
+
export { Button, Column, Columns, Divider, Email, Image, Link, PREHEADER_ATTRIBUTE, Section, Spacer, equalColumnWidth, preheaderNode };
|
package/dist/fmt.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/fmt.d.ts
|
|
2
|
+
declare const DEFAULT_LOCALE = "en-US";
|
|
3
|
+
declare const DEFAULT_TIME_ZONE = "UTC";
|
|
4
|
+
interface FormatContext {
|
|
5
|
+
readonly locale?: string | undefined;
|
|
6
|
+
readonly timeZone?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
type DateStyle = "short" | "medium" | "long" | "full";
|
|
9
|
+
interface PluralForms {
|
|
10
|
+
readonly zero?: string;
|
|
11
|
+
readonly one?: string;
|
|
12
|
+
readonly two?: string;
|
|
13
|
+
readonly few?: string;
|
|
14
|
+
readonly many?: string;
|
|
15
|
+
readonly other: string;
|
|
16
|
+
}
|
|
17
|
+
interface Fmt {
|
|
18
|
+
money(amount: number, currency: string, locale?: string): string;
|
|
19
|
+
number(value: number, options?: Intl.NumberFormatOptions, locale?: string): string;
|
|
20
|
+
date(iso: string, style?: DateStyle, locale?: string): string;
|
|
21
|
+
time(iso: string, style?: DateStyle, locale?: string): string;
|
|
22
|
+
plural(count: number, forms: PluralForms, locale?: string): string;
|
|
23
|
+
list(values: readonly string[], locale?: string): string;
|
|
24
|
+
}
|
|
25
|
+
declare const FORMATTER_NAMES: readonly ["money", "number", "date", "time", "plural", "list"];
|
|
26
|
+
declare const createFmt: (context?: FormatContext) => Fmt;
|
|
27
|
+
declare const withFormatContext: <T>(context: FormatContext, run: () => T) => T;
|
|
28
|
+
declare const fmt: Fmt;
|
|
29
|
+
declare const callFormatter: (formatter: Fmt, name: string, args: readonly unknown[]) => string;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DEFAULT_LOCALE, DEFAULT_TIME_ZONE, DateStyle, FORMATTER_NAMES, Fmt, FormatContext, PluralForms, callFormatter, createFmt, fmt, withFormatContext };
|
|
32
|
+
//# sourceMappingURL=fmt.d.ts.map
|
package/dist/fmt.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
//#region src/fmt.ts
|
|
2
|
+
const DEFAULT_LOCALE = "en-US";
|
|
3
|
+
const DEFAULT_TIME_ZONE = "UTC";
|
|
4
|
+
const FORMATTER_NAMES = [
|
|
5
|
+
"money",
|
|
6
|
+
"number",
|
|
7
|
+
"date",
|
|
8
|
+
"time",
|
|
9
|
+
"plural",
|
|
10
|
+
"list"
|
|
11
|
+
];
|
|
12
|
+
const CACHE_LIMIT = 200;
|
|
13
|
+
const cache = /* @__PURE__ */ new Map();
|
|
14
|
+
const cached = (kind, locale, options, make) => {
|
|
15
|
+
const key = `${kind}\u0000${locale}\u0000${options === void 0 ? "" : JSON.stringify(options)}`;
|
|
16
|
+
const hit = cache.get(key);
|
|
17
|
+
if (hit !== void 0) return hit;
|
|
18
|
+
const value = make();
|
|
19
|
+
if (cache.size >= CACHE_LIMIT) cache.clear();
|
|
20
|
+
cache.set(key, value);
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
const assertFinite = (value, what) => {
|
|
24
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${what} must be a finite number.`);
|
|
25
|
+
};
|
|
26
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
27
|
+
const parseIso = (iso) => {
|
|
28
|
+
if (typeof iso !== "string") throw new TypeError("ISO date must be a string.");
|
|
29
|
+
const date = new Date(DATE_ONLY.test(iso) ? `${iso}T00:00:00Z` : iso);
|
|
30
|
+
if (Number.isNaN(date.getTime())) throw new RangeError(`Cannot parse "${iso}" as an ISO 8601 date.`);
|
|
31
|
+
return date;
|
|
32
|
+
};
|
|
33
|
+
const createFmt = (context = {}) => {
|
|
34
|
+
const defaultLocale = context.locale ?? "en-US";
|
|
35
|
+
const timeZone = context.timeZone ?? "UTC";
|
|
36
|
+
const pick = (locale) => locale ?? defaultLocale;
|
|
37
|
+
return {
|
|
38
|
+
money: (amount, currency, locale) => {
|
|
39
|
+
assertFinite(amount, "money amount");
|
|
40
|
+
const l = pick(locale);
|
|
41
|
+
return cached("money", l, currency, () => new Intl.NumberFormat(l, {
|
|
42
|
+
style: "currency",
|
|
43
|
+
currency
|
|
44
|
+
})).format(amount);
|
|
45
|
+
},
|
|
46
|
+
number: (value, options, locale) => {
|
|
47
|
+
assertFinite(value, "number value");
|
|
48
|
+
const l = pick(locale);
|
|
49
|
+
return cached("number", l, options, () => new Intl.NumberFormat(l, options)).format(value);
|
|
50
|
+
},
|
|
51
|
+
date: (iso, style = "medium", locale) => {
|
|
52
|
+
const d = parseIso(iso);
|
|
53
|
+
const l = pick(locale);
|
|
54
|
+
return cached("date", l, {
|
|
55
|
+
style,
|
|
56
|
+
timeZone
|
|
57
|
+
}, () => new Intl.DateTimeFormat(l, {
|
|
58
|
+
dateStyle: style,
|
|
59
|
+
timeZone
|
|
60
|
+
})).format(d);
|
|
61
|
+
},
|
|
62
|
+
time: (iso, style = "short", locale) => {
|
|
63
|
+
const d = parseIso(iso);
|
|
64
|
+
const l = pick(locale);
|
|
65
|
+
return cached("time", l, {
|
|
66
|
+
style,
|
|
67
|
+
timeZone
|
|
68
|
+
}, () => new Intl.DateTimeFormat(l, {
|
|
69
|
+
timeStyle: style,
|
|
70
|
+
timeZone
|
|
71
|
+
})).format(d);
|
|
72
|
+
},
|
|
73
|
+
plural: (count, forms, locale) => {
|
|
74
|
+
assertFinite(count, "plural count");
|
|
75
|
+
const l = pick(locale);
|
|
76
|
+
return forms[cached("plural", l, void 0, () => new Intl.PluralRules(l)).select(count)] ?? forms.other;
|
|
77
|
+
},
|
|
78
|
+
list: (values, locale) => {
|
|
79
|
+
const l = pick(locale);
|
|
80
|
+
return cached("list", l, void 0, () => new Intl.ListFormat(l, {
|
|
81
|
+
type: "conjunction",
|
|
82
|
+
style: "long"
|
|
83
|
+
})).format(values);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
let ambient = {};
|
|
88
|
+
const withFormatContext = (context, run) => {
|
|
89
|
+
const previous = ambient;
|
|
90
|
+
ambient = context;
|
|
91
|
+
try {
|
|
92
|
+
return run();
|
|
93
|
+
} finally {
|
|
94
|
+
ambient = previous;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const fmt = {
|
|
98
|
+
money: (a, c, l) => createFmt(ambient).money(a, c, l),
|
|
99
|
+
number: (v, o, l) => createFmt(ambient).number(v, o, l),
|
|
100
|
+
date: (i, s, l) => createFmt(ambient).date(i, s, l),
|
|
101
|
+
time: (i, s, l) => createFmt(ambient).time(i, s, l),
|
|
102
|
+
plural: (c, f, l) => createFmt(ambient).plural(c, f, l),
|
|
103
|
+
list: (v, l) => createFmt(ambient).list(v, l)
|
|
104
|
+
};
|
|
105
|
+
const STYLES = [
|
|
106
|
+
"short",
|
|
107
|
+
"medium",
|
|
108
|
+
"long",
|
|
109
|
+
"full"
|
|
110
|
+
];
|
|
111
|
+
const PLURAL_KEYS = [
|
|
112
|
+
"zero",
|
|
113
|
+
"one",
|
|
114
|
+
"two",
|
|
115
|
+
"few",
|
|
116
|
+
"many",
|
|
117
|
+
"other"
|
|
118
|
+
];
|
|
119
|
+
const arity = (name, args, min, max) => {
|
|
120
|
+
if (args.length < min || args.length > max) {
|
|
121
|
+
const expected = min === max ? `${min}` : `${min} to ${max}`;
|
|
122
|
+
throw new TypeError(`${name} takes ${expected} arguments but received ${args.length}.`);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const str = (name, index, value, optional = false) => {
|
|
126
|
+
if (value === void 0 && optional) return void 0;
|
|
127
|
+
if (typeof value !== "string") throw new TypeError(`${name} argument ${index + 1} must be a string.`);
|
|
128
|
+
return value;
|
|
129
|
+
};
|
|
130
|
+
const num = (name, index, value) => {
|
|
131
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new TypeError(`${name} argument ${index + 1} must be a finite number.`);
|
|
132
|
+
return value;
|
|
133
|
+
};
|
|
134
|
+
const style = (name, index, value) => {
|
|
135
|
+
if (value === void 0) return void 0;
|
|
136
|
+
if (typeof value !== "string" || !STYLES.includes(value)) throw new TypeError(`${name} argument ${index + 1} must be one of ${STYLES.join(", ")}.`);
|
|
137
|
+
return value;
|
|
138
|
+
};
|
|
139
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
140
|
+
const callFormatter = (formatter, name, args) => {
|
|
141
|
+
switch (name) {
|
|
142
|
+
case "money":
|
|
143
|
+
arity(name, args, 2, 3);
|
|
144
|
+
return formatter.money(num(name, 0, args[0]), str(name, 1, args[1]), str(name, 2, args[2], true));
|
|
145
|
+
case "number": {
|
|
146
|
+
arity(name, args, 1, 3);
|
|
147
|
+
const options = args[1];
|
|
148
|
+
if (options !== void 0 && !isRecord(options)) throw new TypeError("number argument 2 must be an options object.");
|
|
149
|
+
return formatter.number(num(name, 0, args[0]), options, str(name, 2, args[2], true));
|
|
150
|
+
}
|
|
151
|
+
case "date":
|
|
152
|
+
case "time":
|
|
153
|
+
arity(name, args, 1, 3);
|
|
154
|
+
return formatter[name](str(name, 0, args[0]), style(name, 1, args[1]), str(name, 2, args[2], true));
|
|
155
|
+
case "plural": {
|
|
156
|
+
arity(name, args, 2, 3);
|
|
157
|
+
const forms = args[1];
|
|
158
|
+
if (!isRecord(forms) || typeof forms.other !== "string") throw new TypeError("plural argument 2 must be an object with a string `other` form.");
|
|
159
|
+
for (const key of PLURAL_KEYS) if (forms[key] !== void 0 && typeof forms[key] !== "string") throw new TypeError(`plural argument 2 form \`${key}\` must be a string.`);
|
|
160
|
+
return formatter.plural(num(name, 0, args[0]), forms, str(name, 2, args[2], true));
|
|
161
|
+
}
|
|
162
|
+
case "list": {
|
|
163
|
+
arity(name, args, 1, 2);
|
|
164
|
+
const values = args[0];
|
|
165
|
+
if (!Array.isArray(values) || values.some((v) => typeof v !== "string")) throw new TypeError("list argument 1 must be an array of strings.");
|
|
166
|
+
return formatter.list(values, str(name, 1, args[1], true));
|
|
167
|
+
}
|
|
168
|
+
default: throw new TypeError(`Unknown formatter "${name}".`);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
export { DEFAULT_LOCALE, DEFAULT_TIME_ZONE, FORMATTER_NAMES, callFormatter, createFmt, fmt, withFormatContext };
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=fmt.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED