@weatherboard/cms 0.9.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/README.md CHANGED
@@ -244,9 +244,11 @@ from a later caller read. The returned objects share no caller-owned data.
244
244
  CMS has no runtime dependency on the unpublished menu or schema packages.
245
245
  Compatibility is exercised with a copied, neutral café saved-shape fixture;
246
246
  there is no schema devDependency or menu test-fixture import. Publication tests
247
- resolve imports in source, all eight entry bundles and their build input paths,
247
+ resolve imports in source, all ten entry bundles and their build input paths,
248
248
  including relative imports into either forbidden package root and type specifiers.
249
249
  Type-only test imports are refused too because the copied-fixture option was used.
250
+ When adding a public entry, run the package's whole suite (`pnpm --filter @weatherboard/cms test`)
251
+ and release smoke (`pnpm --filter @weatherboard/cms smoke`) before publication (A-553).
250
252
 
251
253
  ```ts
252
254
  import { menuQuery, menuTags } from '@weatherboard/cms';
@@ -0,0 +1,163 @@
1
+ /** Public contracts only. Evidence and approval identities never enter the CMS document. */
2
+ export declare const FACT_TYPES: readonly ["integer", "decimal", "boolean", "date", "string", "enum", "reference"];
3
+ export declare const FACT_DISPLAY_RULES: readonly ["identity/v1", "floorHundredsPlus/v1"];
4
+ export type FactDisplayRule = typeof FACT_DISPLAY_RULES[number];
5
+ export declare const FACT_KEY: RegExp;
6
+ export declare const FACT_ALIASES: Readonly<Record<string, string>>;
7
+ export type FactValue = {
8
+ type: "integer";
9
+ value: number;
10
+ } | {
11
+ type: "decimal";
12
+ value: string;
13
+ } | {
14
+ type: "boolean";
15
+ value: boolean;
16
+ } | {
17
+ type: "date" | "string";
18
+ value: string;
19
+ } | {
20
+ type: "enum";
21
+ value: string;
22
+ allowedValues: readonly string[];
23
+ } | {
24
+ type: "reference";
25
+ value: {
26
+ documentId: string;
27
+ documentRevision: string;
28
+ };
29
+ };
30
+ export type FactSource = {
31
+ kind: "computed" | "observed" | "attested";
32
+ definition: string;
33
+ version: string;
34
+ };
35
+ export type BusinessFact = {
36
+ key: string;
37
+ publicValue: FactValue;
38
+ unit: string | null;
39
+ timezone: string | null;
40
+ displayRule: FactDisplayRule;
41
+ source: FactSource;
42
+ accountableOwner: string;
43
+ freshness: "computed" | "operational" | "technical";
44
+ optional: boolean;
45
+ surfaces: readonly {
46
+ id: string;
47
+ semantics: "current" | "historical";
48
+ asOf: string | null;
49
+ }[];
50
+ };
51
+ export declare const FACT_EXPIRY_MS: {
52
+ readonly computed: number;
53
+ readonly operational: number;
54
+ readonly technical: number;
55
+ };
56
+ export type FactEvidence = {
57
+ reference: string;
58
+ digest: string;
59
+ observedAt: string;
60
+ expiresAt: string;
61
+ complete: boolean;
62
+ runId: string;
63
+ };
64
+ export type FactRevision = {
65
+ projectId: string;
66
+ revision: number;
67
+ baseRevision: number | null;
68
+ fact: BusinessFact;
69
+ evidence: FactEvidence;
70
+ effectiveAt: string;
71
+ expiresAt: string;
72
+ };
73
+ export type ApprovedFactRevision = FactRevision & {
74
+ hash: string;
75
+ approvedAt: string;
76
+ approval: {
77
+ kind: "signer";
78
+ signerId: string;
79
+ } | {
80
+ kind: "policy";
81
+ policyVersion: string;
82
+ };
83
+ };
84
+ export type FactPublicationReceipt = {
85
+ intentId: string;
86
+ projectId: string;
87
+ revision: number;
88
+ hash: string;
89
+ destination: {
90
+ projectId: string;
91
+ dataset: string;
92
+ };
93
+ expectedDocumentRevision: string | null;
94
+ cms: {
95
+ transactionId: string;
96
+ documentRevision: string;
97
+ readBackHash: string;
98
+ at: string;
99
+ } | null;
100
+ invalidation: {
101
+ deliveryKey: string;
102
+ target: string;
103
+ type: "businessFact";
104
+ id: string;
105
+ at: string;
106
+ } | null;
107
+ render: {
108
+ url: string;
109
+ deploymentId: string;
110
+ revision: number;
111
+ hash: string;
112
+ at: string;
113
+ } | null;
114
+ };
115
+ export type PublicBusinessFact = Pick<BusinessFact, "key" | "publicValue" | "unit" | "timezone" | "displayRule"> & {
116
+ _type: "businessFact";
117
+ _id: string;
118
+ approvedRevision: number;
119
+ approvedHash: string;
120
+ effectiveAt: string;
121
+ expiresAt: string;
122
+ provenanceSummary: string;
123
+ };
124
+ /** A request supplies one snapshot to every surface. Missing and unavailable are distinct from an empty snapshot. */
125
+ export type FactSnapshot = Readonly<Record<string, PublicBusinessFact>>;
126
+ export type FactReadResult = {
127
+ status: "available";
128
+ snapshot: FactSnapshot;
129
+ } | {
130
+ status: "unavailable";
131
+ reason: string;
132
+ };
133
+ export type FactReader = () => Promise<FactReadResult>;
134
+ export type FactPlaceholderContext = "htmlText" | "htmlAttribute" | "json";
135
+ /** Stored Sanity projection: scalar unions use named slots, never private revision fields. */
136
+ export type StoredFactValue = {
137
+ type: FactValue["type"];
138
+ numberValue?: number;
139
+ textValue?: string;
140
+ booleanValue?: boolean;
141
+ allowedValues?: readonly string[];
142
+ documentId?: string;
143
+ documentRevision?: string;
144
+ };
145
+ export declare function parseStoredFactValue(input: unknown): FactValue;
146
+ export declare function storeFactValue(input: FactValue): StoredFactValue;
147
+ export type StoredBusinessFact = Omit<PublicBusinessFact, "publicValue"> & {
148
+ publicValue: StoredFactValue;
149
+ };
150
+ export declare function assertFactKey(key: string): void;
151
+ export declare function businessFactId(key: string): string;
152
+ export declare function parseFactValue(input: unknown): FactValue;
153
+ export declare function parseFactDisplayRule(input: unknown): FactDisplayRule;
154
+ export declare function formatFact(input: FactValue, rule: FactDisplayRule): string;
155
+ /** Escape each replacement, not the surrounding trusted template. JSON returns string content without quotes;
156
+ * callers serialize complete structured objects and keep numeric properties as exact values. Attributes must be quoted.
157
+ * Raw text/React children use the formatter directly; never HTML-unescape an expanded string.
158
+ */
159
+ export declare function expandFactPlaceholders(template: string, values: Readonly<Record<string, string>>, context: FactPlaceholderContext): string;
160
+ /** Fixed JSON domain, sorted object keys, array order preserved, finite numbers only. */
161
+ export declare function canonicalFactJson(value: unknown): string;
162
+ /** SHA-256 of contract fields only; unknown fields and approval are ignored. Instants use UTC milliseconds. */
163
+ export declare function hashFactRevision(revision: FactRevision): Promise<string>;
@@ -0,0 +1,163 @@
1
+ /** Public contracts only. Evidence and approval identities never enter the CMS document. */
2
+ export declare const FACT_TYPES: readonly ["integer", "decimal", "boolean", "date", "string", "enum", "reference"];
3
+ export declare const FACT_DISPLAY_RULES: readonly ["identity/v1", "floorHundredsPlus/v1"];
4
+ export type FactDisplayRule = typeof FACT_DISPLAY_RULES[number];
5
+ export declare const FACT_KEY: RegExp;
6
+ export declare const FACT_ALIASES: Readonly<Record<string, string>>;
7
+ export type FactValue = {
8
+ type: "integer";
9
+ value: number;
10
+ } | {
11
+ type: "decimal";
12
+ value: string;
13
+ } | {
14
+ type: "boolean";
15
+ value: boolean;
16
+ } | {
17
+ type: "date" | "string";
18
+ value: string;
19
+ } | {
20
+ type: "enum";
21
+ value: string;
22
+ allowedValues: readonly string[];
23
+ } | {
24
+ type: "reference";
25
+ value: {
26
+ documentId: string;
27
+ documentRevision: string;
28
+ };
29
+ };
30
+ export type FactSource = {
31
+ kind: "computed" | "observed" | "attested";
32
+ definition: string;
33
+ version: string;
34
+ };
35
+ export type BusinessFact = {
36
+ key: string;
37
+ publicValue: FactValue;
38
+ unit: string | null;
39
+ timezone: string | null;
40
+ displayRule: FactDisplayRule;
41
+ source: FactSource;
42
+ accountableOwner: string;
43
+ freshness: "computed" | "operational" | "technical";
44
+ optional: boolean;
45
+ surfaces: readonly {
46
+ id: string;
47
+ semantics: "current" | "historical";
48
+ asOf: string | null;
49
+ }[];
50
+ };
51
+ export declare const FACT_EXPIRY_MS: {
52
+ readonly computed: number;
53
+ readonly operational: number;
54
+ readonly technical: number;
55
+ };
56
+ export type FactEvidence = {
57
+ reference: string;
58
+ digest: string;
59
+ observedAt: string;
60
+ expiresAt: string;
61
+ complete: boolean;
62
+ runId: string;
63
+ };
64
+ export type FactRevision = {
65
+ projectId: string;
66
+ revision: number;
67
+ baseRevision: number | null;
68
+ fact: BusinessFact;
69
+ evidence: FactEvidence;
70
+ effectiveAt: string;
71
+ expiresAt: string;
72
+ };
73
+ export type ApprovedFactRevision = FactRevision & {
74
+ hash: string;
75
+ approvedAt: string;
76
+ approval: {
77
+ kind: "signer";
78
+ signerId: string;
79
+ } | {
80
+ kind: "policy";
81
+ policyVersion: string;
82
+ };
83
+ };
84
+ export type FactPublicationReceipt = {
85
+ intentId: string;
86
+ projectId: string;
87
+ revision: number;
88
+ hash: string;
89
+ destination: {
90
+ projectId: string;
91
+ dataset: string;
92
+ };
93
+ expectedDocumentRevision: string | null;
94
+ cms: {
95
+ transactionId: string;
96
+ documentRevision: string;
97
+ readBackHash: string;
98
+ at: string;
99
+ } | null;
100
+ invalidation: {
101
+ deliveryKey: string;
102
+ target: string;
103
+ type: "businessFact";
104
+ id: string;
105
+ at: string;
106
+ } | null;
107
+ render: {
108
+ url: string;
109
+ deploymentId: string;
110
+ revision: number;
111
+ hash: string;
112
+ at: string;
113
+ } | null;
114
+ };
115
+ export type PublicBusinessFact = Pick<BusinessFact, "key" | "publicValue" | "unit" | "timezone" | "displayRule"> & {
116
+ _type: "businessFact";
117
+ _id: string;
118
+ approvedRevision: number;
119
+ approvedHash: string;
120
+ effectiveAt: string;
121
+ expiresAt: string;
122
+ provenanceSummary: string;
123
+ };
124
+ /** A request supplies one snapshot to every surface. Missing and unavailable are distinct from an empty snapshot. */
125
+ export type FactSnapshot = Readonly<Record<string, PublicBusinessFact>>;
126
+ export type FactReadResult = {
127
+ status: "available";
128
+ snapshot: FactSnapshot;
129
+ } | {
130
+ status: "unavailable";
131
+ reason: string;
132
+ };
133
+ export type FactReader = () => Promise<FactReadResult>;
134
+ export type FactPlaceholderContext = "htmlText" | "htmlAttribute" | "json";
135
+ /** Stored Sanity projection: scalar unions use named slots, never private revision fields. */
136
+ export type StoredFactValue = {
137
+ type: FactValue["type"];
138
+ numberValue?: number;
139
+ textValue?: string;
140
+ booleanValue?: boolean;
141
+ allowedValues?: readonly string[];
142
+ documentId?: string;
143
+ documentRevision?: string;
144
+ };
145
+ export declare function parseStoredFactValue(input: unknown): FactValue;
146
+ export declare function storeFactValue(input: FactValue): StoredFactValue;
147
+ export type StoredBusinessFact = Omit<PublicBusinessFact, "publicValue"> & {
148
+ publicValue: StoredFactValue;
149
+ };
150
+ export declare function assertFactKey(key: string): void;
151
+ export declare function businessFactId(key: string): string;
152
+ export declare function parseFactValue(input: unknown): FactValue;
153
+ export declare function parseFactDisplayRule(input: unknown): FactDisplayRule;
154
+ export declare function formatFact(input: FactValue, rule: FactDisplayRule): string;
155
+ /** Escape each replacement, not the surrounding trusted template. JSON returns string content without quotes;
156
+ * callers serialize complete structured objects and keep numeric properties as exact values. Attributes must be quoted.
157
+ * Raw text/React children use the formatter directly; never HTML-unescape an expanded string.
158
+ */
159
+ export declare function expandFactPlaceholders(template: string, values: Readonly<Record<string, string>>, context: FactPlaceholderContext): string;
160
+ /** Fixed JSON domain, sorted object keys, array order preserved, finite numbers only. */
161
+ export declare function canonicalFactJson(value: unknown): string;
162
+ /** SHA-256 of contract fields only; unknown fields and approval are ignored. Instants use UTC milliseconds. */
163
+ export declare function hashFactRevision(revision: FactRevision): Promise<string>;
@@ -0,0 +1,163 @@
1
+ /** Public contracts only. Evidence and approval identities never enter the CMS document. */
2
+ export declare const FACT_TYPES: readonly ["integer", "decimal", "boolean", "date", "string", "enum", "reference"];
3
+ export declare const FACT_DISPLAY_RULES: readonly ["identity/v1", "floorHundredsPlus/v1"];
4
+ export type FactDisplayRule = typeof FACT_DISPLAY_RULES[number];
5
+ export declare const FACT_KEY: RegExp;
6
+ export declare const FACT_ALIASES: Readonly<Record<string, string>>;
7
+ export type FactValue = {
8
+ type: "integer";
9
+ value: number;
10
+ } | {
11
+ type: "decimal";
12
+ value: string;
13
+ } | {
14
+ type: "boolean";
15
+ value: boolean;
16
+ } | {
17
+ type: "date" | "string";
18
+ value: string;
19
+ } | {
20
+ type: "enum";
21
+ value: string;
22
+ allowedValues: readonly string[];
23
+ } | {
24
+ type: "reference";
25
+ value: {
26
+ documentId: string;
27
+ documentRevision: string;
28
+ };
29
+ };
30
+ export type FactSource = {
31
+ kind: "computed" | "observed" | "attested";
32
+ definition: string;
33
+ version: string;
34
+ };
35
+ export type BusinessFact = {
36
+ key: string;
37
+ publicValue: FactValue;
38
+ unit: string | null;
39
+ timezone: string | null;
40
+ displayRule: FactDisplayRule;
41
+ source: FactSource;
42
+ accountableOwner: string;
43
+ freshness: "computed" | "operational" | "technical";
44
+ optional: boolean;
45
+ surfaces: readonly {
46
+ id: string;
47
+ semantics: "current" | "historical";
48
+ asOf: string | null;
49
+ }[];
50
+ };
51
+ export declare const FACT_EXPIRY_MS: {
52
+ readonly computed: number;
53
+ readonly operational: number;
54
+ readonly technical: number;
55
+ };
56
+ export type FactEvidence = {
57
+ reference: string;
58
+ digest: string;
59
+ observedAt: string;
60
+ expiresAt: string;
61
+ complete: boolean;
62
+ runId: string;
63
+ };
64
+ export type FactRevision = {
65
+ projectId: string;
66
+ revision: number;
67
+ baseRevision: number | null;
68
+ fact: BusinessFact;
69
+ evidence: FactEvidence;
70
+ effectiveAt: string;
71
+ expiresAt: string;
72
+ };
73
+ export type ApprovedFactRevision = FactRevision & {
74
+ hash: string;
75
+ approvedAt: string;
76
+ approval: {
77
+ kind: "signer";
78
+ signerId: string;
79
+ } | {
80
+ kind: "policy";
81
+ policyVersion: string;
82
+ };
83
+ };
84
+ export type FactPublicationReceipt = {
85
+ intentId: string;
86
+ projectId: string;
87
+ revision: number;
88
+ hash: string;
89
+ destination: {
90
+ projectId: string;
91
+ dataset: string;
92
+ };
93
+ expectedDocumentRevision: string | null;
94
+ cms: {
95
+ transactionId: string;
96
+ documentRevision: string;
97
+ readBackHash: string;
98
+ at: string;
99
+ } | null;
100
+ invalidation: {
101
+ deliveryKey: string;
102
+ target: string;
103
+ type: "businessFact";
104
+ id: string;
105
+ at: string;
106
+ } | null;
107
+ render: {
108
+ url: string;
109
+ deploymentId: string;
110
+ revision: number;
111
+ hash: string;
112
+ at: string;
113
+ } | null;
114
+ };
115
+ export type PublicBusinessFact = Pick<BusinessFact, "key" | "publicValue" | "unit" | "timezone" | "displayRule"> & {
116
+ _type: "businessFact";
117
+ _id: string;
118
+ approvedRevision: number;
119
+ approvedHash: string;
120
+ effectiveAt: string;
121
+ expiresAt: string;
122
+ provenanceSummary: string;
123
+ };
124
+ /** A request supplies one snapshot to every surface. Missing and unavailable are distinct from an empty snapshot. */
125
+ export type FactSnapshot = Readonly<Record<string, PublicBusinessFact>>;
126
+ export type FactReadResult = {
127
+ status: "available";
128
+ snapshot: FactSnapshot;
129
+ } | {
130
+ status: "unavailable";
131
+ reason: string;
132
+ };
133
+ export type FactReader = () => Promise<FactReadResult>;
134
+ export type FactPlaceholderContext = "htmlText" | "htmlAttribute" | "json";
135
+ /** Stored Sanity projection: scalar unions use named slots, never private revision fields. */
136
+ export type StoredFactValue = {
137
+ type: FactValue["type"];
138
+ numberValue?: number;
139
+ textValue?: string;
140
+ booleanValue?: boolean;
141
+ allowedValues?: readonly string[];
142
+ documentId?: string;
143
+ documentRevision?: string;
144
+ };
145
+ export declare function parseStoredFactValue(input: unknown): FactValue;
146
+ export declare function storeFactValue(input: FactValue): StoredFactValue;
147
+ export type StoredBusinessFact = Omit<PublicBusinessFact, "publicValue"> & {
148
+ publicValue: StoredFactValue;
149
+ };
150
+ export declare function assertFactKey(key: string): void;
151
+ export declare function businessFactId(key: string): string;
152
+ export declare function parseFactValue(input: unknown): FactValue;
153
+ export declare function parseFactDisplayRule(input: unknown): FactDisplayRule;
154
+ export declare function formatFact(input: FactValue, rule: FactDisplayRule): string;
155
+ /** Escape each replacement, not the surrounding trusted template. JSON returns string content without quotes;
156
+ * callers serialize complete structured objects and keep numeric properties as exact values. Attributes must be quoted.
157
+ * Raw text/React children use the formatter directly; never HTML-unescape an expanded string.
158
+ */
159
+ export declare function expandFactPlaceholders(template: string, values: Readonly<Record<string, string>>, context: FactPlaceholderContext): string;
160
+ /** Fixed JSON domain, sorted object keys, array order preserved, finite numbers only. */
161
+ export declare function canonicalFactJson(value: unknown): string;
162
+ /** SHA-256 of contract fields only; unknown fields and approval are ignored. Instants use UTC milliseconds. */
163
+ export declare function hashFactRevision(revision: FactRevision): Promise<string>;
@@ -0,0 +1,2 @@
1
+ "use strict";var n=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var o=(s,t)=>{for(var e in t)n(s,e,{get:t[e],enumerable:!0})},g=(s,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of c(t))!l.call(s,r)&&r!==e&&n(s,r,{get:()=>t[r],enumerable:!(a=i(t,r))||a.enumerable});return s};var d=s=>g(n({},"__esModule",{value:!0}),s);var f={};o(f,{FACT_SURFACE_MANIFEST_VERSION:()=>y});module.exports=d(f);var y=1;0&&(module.exports={FACT_SURFACE_MANIFEST_VERSION});
2
+ //# sourceMappingURL=fact-guard.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/fact-guard.ts"],
4
+ "sourcesContent": ["/** Registration seam for the later manifest guard; historical exclusions require evidence. */\nexport const FACT_SURFACE_MANIFEST_VERSION = 1 as const;\nexport type FactSurfaceManifest = {\n facts: readonly { key: string; literals: readonly string[] }[];\n surfaces: readonly { id: string; path: string; semantics: \"current\" | \"historical\" }[];\n exclusions: readonly { path: string; literal: string; asOf: string; evidence: string }[];\n};\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mCAAAE,IAAA,eAAAC,EAAAH,GACO,IAAME,EAAgC",
6
+ "names": ["fact_guard_exports", "__export", "FACT_SURFACE_MANIFEST_VERSION", "__toCommonJS"]
7
+ }
@@ -0,0 +1,19 @@
1
+ /** Registration seam for the later manifest guard; historical exclusions require evidence. */
2
+ export declare const FACT_SURFACE_MANIFEST_VERSION: 1;
3
+ export type FactSurfaceManifest = {
4
+ facts: readonly {
5
+ key: string;
6
+ literals: readonly string[];
7
+ }[];
8
+ surfaces: readonly {
9
+ id: string;
10
+ path: string;
11
+ semantics: "current" | "historical";
12
+ }[];
13
+ exclusions: readonly {
14
+ path: string;
15
+ literal: string;
16
+ asOf: string;
17
+ evidence: string;
18
+ }[];
19
+ };
@@ -0,0 +1,19 @@
1
+ /** Registration seam for the later manifest guard; historical exclusions require evidence. */
2
+ export declare const FACT_SURFACE_MANIFEST_VERSION: 1;
3
+ export type FactSurfaceManifest = {
4
+ facts: readonly {
5
+ key: string;
6
+ literals: readonly string[];
7
+ }[];
8
+ surfaces: readonly {
9
+ id: string;
10
+ path: string;
11
+ semantics: "current" | "historical";
12
+ }[];
13
+ exclusions: readonly {
14
+ path: string;
15
+ literal: string;
16
+ asOf: string;
17
+ evidence: string;
18
+ }[];
19
+ };
@@ -0,0 +1,19 @@
1
+ /** Registration seam for the later manifest guard; historical exclusions require evidence. */
2
+ export declare const FACT_SURFACE_MANIFEST_VERSION: 1;
3
+ export type FactSurfaceManifest = {
4
+ facts: readonly {
5
+ key: string;
6
+ literals: readonly string[];
7
+ }[];
8
+ surfaces: readonly {
9
+ id: string;
10
+ path: string;
11
+ semantics: "current" | "historical";
12
+ }[];
13
+ exclusions: readonly {
14
+ path: string;
15
+ literal: string;
16
+ asOf: string;
17
+ evidence: string;
18
+ }[];
19
+ };
@@ -0,0 +1,2 @@
1
+ var t=1;export{t as FACT_SURFACE_MANIFEST_VERSION};
2
+ //# sourceMappingURL=fact-guard.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/fact-guard.ts"],
4
+ "sourcesContent": ["/** Registration seam for the later manifest guard; historical exclusions require evidence. */\nexport const FACT_SURFACE_MANIFEST_VERSION = 1 as const;\nexport type FactSurfaceManifest = {\n facts: readonly { key: string; literals: readonly string[] }[];\n surfaces: readonly { id: string; path: string; semantics: \"current\" | \"historical\" }[];\n exclusions: readonly { path: string; literal: string; asOf: string; evidence: string }[];\n};\n"],
5
+ "mappings": "AACO,IAAMA,EAAgC",
6
+ "names": ["FACT_SURFACE_MANIFEST_VERSION"]
7
+ }
package/dist/facts.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var d=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var S=(e,t)=>{for(var n in t)d(e,n,{get:t[n],enumerable:!0})},w=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of R(t))!A.call(e,a)&&a!==n&&d(e,a,{get:()=>t[a],enumerable:!(r=x(t,a))||r.enumerable});return e};var h=e=>w(d({},"__esModule",{value:!0}),e);var k={};S(k,{FACT_ALIASES:()=>g,FACT_DISPLAY_RULES:()=>I,FACT_EXPIRY_MS:()=>m,FACT_KEY:()=>y,FACT_TYPES:()=>V,assertFactKey:()=>p,businessFactId:()=>D,canonicalFactJson:()=>l,expandFactPlaceholders:()=>P,formatFact:()=>v,hashFactRevision:()=>_,parseFactDisplayRule:()=>b,parseFactValue:()=>c,parseStoredFactValue:()=>O,storeFactValue:()=>j});module.exports=h(k);var V=["integer","decimal","boolean","date","string","enum","reference"],I=["identity/v1","floorHundredsPlus/v1"],y=/^[a-z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]*)*$/,g={pubCount:"coverage.pubCount"},m={computed:72*36e5,operational:30*864e5,technical:90*864e5};function O(e){let t=f(e),n=t.type==="integer"?t.numberValue:t.type==="boolean"?t.booleanValue:t.type==="reference"?{documentId:t.documentId,documentRevision:t.documentRevision}:t.textValue;return c({type:t.type,value:n,allowedValues:t.allowedValues})}function j(e){let t=c(e);return t.type==="integer"?{type:t.type,numberValue:t.value}:t.type==="boolean"?{type:t.type,booleanValue:t.value}:t.type==="reference"?{type:t.type,...t.value}:t.type==="enum"?{type:t.type,textValue:t.value,allowedValues:t.allowedValues}:{type:t.type,textValue:t.value}}function s(){throw new Error("Invalid business fact contract")}function f(e){return!e||typeof e!="object"||Array.isArray(e)?s():e}function o(e){return typeof e=="string"&&e.trim()!==""?e:s()}function p(e){(e.length>48||!y.test(e))&&s()}function D(e){return p(e),"businessFact-"+Array.from(new TextEncoder().encode(e),t=>t.toString(16).padStart(2,"0")).join("")}function c(e){let t=f(e),n=t.value;switch(t.type){case"integer":return Number.isSafeInteger(n)?{type:"integer",value:n}:s();case"decimal":return typeof n=="string"&&/^(?:0|-?[1-9]\d*)(?:\.\d*[1-9])?$|^-0\.\d*[1-9]$/.test(n)?{type:"decimal",value:n}:s();case"boolean":return typeof n=="boolean"?{type:"boolean",value:n}:s();case"string":return typeof n=="string"?{type:"string",value:n}:s();case"date":return typeof n=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(n)&&!Number.isNaN(Date.parse(n))&&new Date(n).toISOString().slice(0,10)===n?{type:"date",value:n}:s();case"enum":{let r=t.allowedValues;return!Array.isArray(r)||!r.length||!r.every(a=>typeof a=="string"&&a!=="")||new Set(r).size!==r.length||!r.includes(n)?s():{type:"enum",value:o(n),allowedValues:[...r].sort()}}case"reference":{let r=f(n);return{type:"reference",value:{documentId:o(r.documentId),documentRevision:o(r.documentRevision)}}}default:return s()}}function b(e){return e!=="identity/v1"&&e!=="floorHundredsPlus/v1"?s():e}function v(e,t){let n=c(e);return b(t)==="floorHundredsPlus/v1"?n.type!=="integer"||n.value<0?s():n.value<100?String(n.value):`${(Math.floor(n.value/100)*100).toLocaleString("en-GB")}+`:n.type==="reference"?n.value.documentId:String(n.value)}function P(e,t,n){return["htmlText","htmlAttribute","json"].includes(n)?e.replace(/\{([^{}]*)\}/g,(r,a)=>{p(a);let u=Object.hasOwn(g,a)?g[a]:a;if(!Object.hasOwn(t,u))throw new Error(`Unknown fact placeholder: ${a}`);let i=t[u];return n==="json"?JSON.stringify(i).slice(1,-1).replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029"):i.replace(/[&<>"']/g,F=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[F])}):s()}function l(e){return e===null||typeof e=="boolean"||typeof e=="string"||typeof e=="number"&&Number.isFinite(e)?JSON.stringify(e):Array.isArray(e)?`[${Array.from(e,l).join(",")}]`:typeof e!="object"||e===null||Object.getPrototypeOf(e)!==Object.prototype?s():`{${Object.entries(e).sort(([t],[n])=>t<n?-1:t>n?1:0).map(([t,n])=>`${JSON.stringify(t)}:${l(n)}`).join(",")}}`}async function _(e){let t=e.fact;p(t.key);let n=c(t.publicValue);v(n,t.displayRule),(!Number.isSafeInteger(e.revision)||e.revision<1||e.baseRevision!==null&&(!Number.isSafeInteger(e.baseRevision)||e.baseRevision<1||e.baseRevision>=e.revision))&&s(),o(e.projectId),o(t.accountableOwner),o(t.source.definition),o(t.source.version),(!["computed","observed","attested"].includes(t.source.kind)||!Object.hasOwn(m,t.freshness)||typeof t.optional!="boolean"||t.unit!==null&&typeof t.unit!="string")&&s(),t.timezone!==null&&new Intl.DateTimeFormat("en-GB",{timeZone:o(t.timezone)});let r=i=>((!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(i)||!Number.isFinite(Date.parse(i))||new Date(i).toISOString()!==i.replace(/(?<=:\d{2})Z$/,".000Z"))&&s(),Date.parse(i));(r(e.expiresAt)<=r(e.effectiveAt)||r(e.evidence.expiresAt)<=r(e.evidence.observedAt)||!/^[0-9a-f]{64}$/.test(e.evidence.digest)||typeof e.evidence.complete!="boolean")&&s(),o(e.evidence.reference),o(e.evidence.runId);for(let i of t.surfaces)o(i.id),i.semantics==="historical"?c({type:"date",value:i.asOf}):(i.semantics!=="current"||i.asOf!==null)&&s();let a={projectId:e.projectId,revision:e.revision,baseRevision:e.baseRevision,fact:{key:t.key,publicValue:n,unit:t.unit,timezone:t.timezone,displayRule:t.displayRule,source:{kind:t.source.kind,definition:t.source.definition,version:t.source.version},accountableOwner:t.accountableOwner,freshness:t.freshness,optional:t.optional,surfaces:t.surfaces.map(i=>({id:i.id,semantics:i.semantics,asOf:i.asOf}))},evidence:{reference:e.evidence.reference,digest:e.evidence.digest,complete:e.evidence.complete,runId:e.evidence.runId,observedAt:new Date(r(e.evidence.observedAt)).toISOString(),expiresAt:new Date(r(e.evidence.expiresAt)).toISOString()},effectiveAt:new Date(r(e.effectiveAt)).toISOString(),expiresAt:new Date(r(e.expiresAt)).toISOString()},u=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(l(a)));return Array.from(new Uint8Array(u),i=>i.toString(16).padStart(2,"0")).join("")}0&&(module.exports={FACT_ALIASES,FACT_DISPLAY_RULES,FACT_EXPIRY_MS,FACT_KEY,FACT_TYPES,assertFactKey,businessFactId,canonicalFactJson,expandFactPlaceholders,formatFact,hashFactRevision,parseFactDisplayRule,parseFactValue,parseStoredFactValue,storeFactValue});
2
+ //# sourceMappingURL=facts.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/facts.ts", "../src/contracts/facts.ts"],
4
+ "sourcesContent": ["// Dedicated entry reserved for the published snapshot reader; no runtime transport in this foundation.\nexport * from \"./contracts/facts.js\";\n", "/** Public contracts only. Evidence and approval identities never enter the CMS document. */\nexport const FACT_TYPES = [\"integer\", \"decimal\", \"boolean\", \"date\", \"string\", \"enum\", \"reference\"] as const;\nexport const FACT_DISPLAY_RULES = [\"identity/v1\", \"floorHundredsPlus/v1\"] as const;\nexport type FactDisplayRule = typeof FACT_DISPLAY_RULES[number];\nexport const FACT_KEY = /^[a-z][a-zA-Z0-9]*(?:\\.[a-zA-Z][a-zA-Z0-9]*)*$/;\nexport const FACT_ALIASES: Readonly<Record<string, string>> = { pubCount: \"coverage.pubCount\" };\nexport type FactValue =\n | { type: \"integer\"; value: number }\n // Decimal strings avoid binary floating-point loss; canonical form has no trailing fractional zeroes.\n | { type: \"decimal\"; value: string }\n | { type: \"boolean\"; value: boolean }\n | { type: \"date\" | \"string\"; value: string }\n | { type: \"enum\"; value: string; allowedValues: readonly string[] }\n | { type: \"reference\"; value: { documentId: string; documentRevision: string } };\nexport type FactSource = { kind: \"computed\" | \"observed\" | \"attested\"; definition: string; version: string };\nexport type BusinessFact = {\n key: string; publicValue: FactValue; unit: string | null; timezone: string | null;\n displayRule: FactDisplayRule; source: FactSource; accountableOwner: string;\n freshness: \"computed\" | \"operational\" | \"technical\"; optional: boolean;\n surfaces: readonly { id: string; semantics: \"current\" | \"historical\"; asOf: string | null }[];\n};\nexport const FACT_EXPIRY_MS = { computed: 72 * 3_600_000, operational: 30 * 86_400_000, technical: 90 * 86_400_000 } as const;\nexport type FactEvidence = { reference: string; digest: string; observedAt: string; expiresAt: string; complete: boolean; runId: string };\nexport type FactRevision = {\n projectId: string; revision: number; baseRevision: number | null; fact: BusinessFact;\n evidence: FactEvidence; effectiveAt: string; expiresAt: string;\n};\nexport type ApprovedFactRevision = FactRevision & {\n hash: string; approvedAt: string;\n approval: { kind: \"signer\"; signerId: string } | { kind: \"policy\"; policyVersion: string };\n};\nexport type FactPublicationReceipt = {\n intentId: string; projectId: string; revision: number; hash: string;\n destination: { projectId: string; dataset: string }; expectedDocumentRevision: string | null;\n cms: { transactionId: string; documentRevision: string; readBackHash: string; at: string } | null;\n invalidation: { deliveryKey: string; target: string; type: \"businessFact\"; id: string; at: string } | null;\n render: { url: string; deploymentId: string; revision: number; hash: string; at: string } | null;\n};\nexport type PublicBusinessFact = Pick<BusinessFact, \"key\" | \"publicValue\" | \"unit\" | \"timezone\" | \"displayRule\"> & {\n _type: \"businessFact\"; _id: string; approvedRevision: number; approvedHash: string;\n effectiveAt: string; expiresAt: string; provenanceSummary: string;\n};\n/** A request supplies one snapshot to every surface. Missing and unavailable are distinct from an empty snapshot. */\nexport type FactSnapshot = Readonly<Record<string, PublicBusinessFact>>;\nexport type FactReadResult = { status: \"available\"; snapshot: FactSnapshot } | { status: \"unavailable\"; reason: string };\nexport type FactReader = () => Promise<FactReadResult>;\nexport type FactPlaceholderContext = \"htmlText\" | \"htmlAttribute\" | \"json\";\n/** Stored Sanity projection: scalar unions use named slots, never private revision fields. */\nexport type StoredFactValue = { type: FactValue[\"type\"]; numberValue?: number; textValue?: string;\n booleanValue?: boolean; allowedValues?: readonly string[]; documentId?: string; documentRevision?: string };\nexport function parseStoredFactValue(input: unknown): FactValue {\n const entry = record(input);\n const value = entry.type === \"integer\" ? entry.numberValue : entry.type === \"boolean\" ? entry.booleanValue\n : entry.type === \"reference\" ? { documentId: entry.documentId, documentRevision: entry.documentRevision } : entry.textValue;\n return parseFactValue({ type: entry.type, value, allowedValues: entry.allowedValues });\n}\nexport function storeFactValue(input: FactValue): StoredFactValue {\n const entry = parseFactValue(input);\n if (entry.type === \"integer\") return { type: entry.type, numberValue: entry.value };\n if (entry.type === \"boolean\") return { type: entry.type, booleanValue: entry.value };\n if (entry.type === \"reference\") return { type: entry.type, ...entry.value };\n return entry.type === \"enum\" ? { type: entry.type, textValue: entry.value, allowedValues: entry.allowedValues }\n : { type: entry.type, textValue: entry.value };\n}\nexport type StoredBusinessFact = Omit<PublicBusinessFact, \"publicValue\"> & { publicValue: StoredFactValue };\n\nfunction invalid(): never { throw new Error(\"Invalid business fact contract\"); }\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return invalid();\n return value as Record<string, unknown>;\n}\nfunction text(value: unknown): string { return typeof value === \"string\" && value.trim() !== \"\" ? value : invalid(); }\nexport function assertFactKey(key: string): void {\n if (key.length > 48 || !FACT_KEY.test(key)) invalid();\n}\nexport function businessFactId(key: string): string {\n assertFactKey(key);\n // Dataset-local, injective encoding; never a draft ID or a key containing a path separator.\n return \"businessFact-\" + Array.from(new TextEncoder().encode(key), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nexport function parseFactValue(input: unknown): FactValue {\n const entry = record(input), value = entry.value;\n switch (entry.type) {\n case \"integer\": return Number.isSafeInteger(value) ? { type: \"integer\", value: value as number } : invalid();\n case \"decimal\": return typeof value === \"string\" && /^(?:0|-?[1-9]\\d*)(?:\\.\\d*[1-9])?$|^-0\\.\\d*[1-9]$/.test(value)\n ? { type: \"decimal\", value } : invalid();\n case \"boolean\": return typeof value === \"boolean\" ? { type: \"boolean\", value } : invalid();\n case \"string\": return typeof value === \"string\" ? { type: \"string\", value } : invalid();\n case \"date\": return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)\n && !Number.isNaN(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value ? { type: \"date\", value } : invalid();\n case \"enum\": {\n const allowed = entry.allowedValues;\n if (!Array.isArray(allowed) || !allowed.length || !allowed.every((item) => typeof item === \"string\" && item !== \"\")\n || new Set(allowed).size !== allowed.length || !allowed.includes(value)) return invalid();\n return { type: \"enum\", value: text(value), allowedValues: [...allowed].sort() as string[] };\n }\n case \"reference\": {\n const ref = record(value);\n return { type: \"reference\", value: { documentId: text(ref.documentId), documentRevision: text(ref.documentRevision) } };\n }\n default: return invalid();\n }\n}\nexport function parseFactDisplayRule(input: unknown): FactDisplayRule {\n if (input !== \"identity/v1\" && input !== \"floorHundredsPlus/v1\") return invalid();\n return input;\n}\nexport function formatFact(input: FactValue, rule: FactDisplayRule): string {\n const value = parseFactValue(input);\n if (parseFactDisplayRule(rule) === \"floorHundredsPlus/v1\") {\n if (value.type !== \"integer\" || value.value < 0) return invalid();\n return value.value < 100 ? String(value.value) : `${(Math.floor(value.value / 100) * 100).toLocaleString(\"en-GB\")}+`;\n }\n return value.type === \"reference\" ? value.value.documentId : String(value.value);\n}\n/** Escape each replacement, not the surrounding trusted template. JSON returns string content without quotes;\n * callers serialize complete structured objects and keep numeric properties as exact values. Attributes must be quoted.\n * Raw text/React children use the formatter directly; never HTML-unescape an expanded string.\n */\nexport function expandFactPlaceholders(template: string, values: Readonly<Record<string, string>>, context: FactPlaceholderContext): string {\n if (![\"htmlText\", \"htmlAttribute\", \"json\"].includes(context)) return invalid();\n return template.replace(/\\{([^{}]*)\\}/g, (_match, key: string) => {\n assertFactKey(key);\n const resolved = Object.hasOwn(FACT_ALIASES, key) ? FACT_ALIASES[key]! : key;\n if (!Object.hasOwn(values, resolved)) throw new Error(`Unknown fact placeholder: ${key}`);\n const value = values[resolved]!;\n if (context === \"json\") return JSON.stringify(value).slice(1, -1)\n .replace(/</g, \"\\\\u003c\").replace(/>/g, \"\\\\u003e\").replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\").replace(/\\u2029/g, \"\\\\u2029\");\n return value.replace(/[&<>\"']/g, (char) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[char]!);\n });\n}\n/** Fixed JSON domain, sorted object keys, array order preserved, finite numbers only. */\nexport function canonicalFactJson(value: unknown): string {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") return JSON.stringify(value);\n if (typeof value === \"number\" && Number.isFinite(value)) return JSON.stringify(value);\n if (Array.isArray(value)) return `[${Array.from(value, canonicalFactJson).join(\",\")}]`;\n if (typeof value !== \"object\" || value === null || Object.getPrototypeOf(value) !== Object.prototype) return invalid();\n return `{${Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)\n .map(([key, item]) => `${JSON.stringify(key)}:${canonicalFactJson(item)}`).join(\",\")}}`;\n}\n/** SHA-256 of contract fields only; unknown fields and approval are ignored. Instants use UTC milliseconds. */\nexport async function hashFactRevision(revision: FactRevision): Promise<string> {\n const fact = revision.fact;\n assertFactKey(fact.key);\n const publicValue = parseFactValue(fact.publicValue);\n formatFact(publicValue, fact.displayRule);\n if (!Number.isSafeInteger(revision.revision) || revision.revision < 1\n || (revision.baseRevision !== null && (!Number.isSafeInteger(revision.baseRevision) || revision.baseRevision < 1 || revision.baseRevision >= revision.revision))) invalid();\n text(revision.projectId); text(fact.accountableOwner); text(fact.source.definition); text(fact.source.version);\n if (![\"computed\", \"observed\", \"attested\"].includes(fact.source.kind) || !Object.hasOwn(FACT_EXPIRY_MS, fact.freshness)\n || typeof fact.optional !== \"boolean\" || (fact.unit !== null && typeof fact.unit !== \"string\")) invalid();\n if (fact.timezone !== null) new Intl.DateTimeFormat(\"en-GB\", { timeZone: text(fact.timezone) });\n const instant = (value: string) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/.test(value) || !Number.isFinite(Date.parse(value))\n || new Date(value).toISOString() !== value.replace(/(?<=:\\d{2})Z$/, \".000Z\")) invalid();\n return Date.parse(value);\n };\n if (instant(revision.expiresAt) <= instant(revision.effectiveAt)\n || instant(revision.evidence.expiresAt) <= instant(revision.evidence.observedAt)\n || !/^[0-9a-f]{64}$/.test(revision.evidence.digest) || typeof revision.evidence.complete !== \"boolean\") invalid();\n text(revision.evidence.reference); text(revision.evidence.runId);\n for (const surface of fact.surfaces) {\n text(surface.id);\n if (surface.semantics === \"historical\") parseFactValue({ type: \"date\", value: surface.asOf });\n else if (surface.semantics !== \"current\" || surface.asOf !== null) invalid();\n }\n const candidate: FactRevision = {\n projectId: revision.projectId, revision: revision.revision, baseRevision: revision.baseRevision,\n fact: {\n key: fact.key, publicValue, unit: fact.unit, timezone: fact.timezone, displayRule: fact.displayRule,\n source: { kind: fact.source.kind, definition: fact.source.definition, version: fact.source.version },\n accountableOwner: fact.accountableOwner, freshness: fact.freshness, optional: fact.optional,\n surfaces: fact.surfaces.map((surface) => ({ id: surface.id, semantics: surface.semantics, asOf: surface.asOf })),\n },\n evidence: {\n reference: revision.evidence.reference, digest: revision.evidence.digest, complete: revision.evidence.complete,\n runId: revision.evidence.runId, observedAt: new Date(instant(revision.evidence.observedAt)).toISOString(),\n expiresAt: new Date(instant(revision.evidence.expiresAt)).toISOString(),\n },\n effectiveAt: new Date(instant(revision.effectiveAt)).toISOString(),\n expiresAt: new Date(instant(revision.expiresAt)).toISOString(),\n };\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(canonicalFactJson(candidate)));\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["facts_exports", "__export", "FACT_ALIASES", "FACT_DISPLAY_RULES", "FACT_EXPIRY_MS", "FACT_KEY", "FACT_TYPES", "assertFactKey", "businessFactId", "canonicalFactJson", "expandFactPlaceholders", "formatFact", "hashFactRevision", "parseFactDisplayRule", "parseFactValue", "parseStoredFactValue", "storeFactValue", "__toCommonJS", "FACT_TYPES", "FACT_DISPLAY_RULES", "FACT_KEY", "FACT_ALIASES", "FACT_EXPIRY_MS", "parseStoredFactValue", "input", "entry", "record", "value", "parseFactValue", "storeFactValue", "invalid", "text", "assertFactKey", "key", "businessFactId", "byte", "allowed", "item", "ref", "parseFactDisplayRule", "formatFact", "rule", "expandFactPlaceholders", "template", "values", "context", "_match", "resolved", "char", "canonicalFactJson", "a", "b", "hashFactRevision", "revision", "fact", "publicValue", "instant", "surface", "candidate", "digest"]
7
+ }
@@ -0,0 +1 @@
1
+ export * from "./contracts/facts.cjs";
@@ -0,0 +1 @@
1
+ export * from "./contracts/facts.mjs";
@@ -0,0 +1 @@
1
+ export * from "./contracts/facts.js";
package/dist/facts.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var F=["integer","decimal","boolean","date","string","enum","reference"],x=["identity/v1","floorHundredsPlus/v1"],y=/^[a-z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]*)*$/,g={pubCount:"coverage.pubCount"},m={computed:72*36e5,operational:30*864e5,technical:90*864e5};function R(e){let t=l(e),n=t.type==="integer"?t.numberValue:t.type==="boolean"?t.booleanValue:t.type==="reference"?{documentId:t.documentId,documentRevision:t.documentRevision}:t.textValue;return c({type:t.type,value:n,allowedValues:t.allowedValues})}function A(e){let t=c(e);return t.type==="integer"?{type:t.type,numberValue:t.value}:t.type==="boolean"?{type:t.type,booleanValue:t.value}:t.type==="reference"?{type:t.type,...t.value}:t.type==="enum"?{type:t.type,textValue:t.value,allowedValues:t.allowedValues}:{type:t.type,textValue:t.value}}function s(){throw new Error("Invalid business fact contract")}function l(e){return!e||typeof e!="object"||Array.isArray(e)?s():e}function a(e){return typeof e=="string"&&e.trim()!==""?e:s()}function d(e){(e.length>48||!y.test(e))&&s()}function S(e){return d(e),"businessFact-"+Array.from(new TextEncoder().encode(e),t=>t.toString(16).padStart(2,"0")).join("")}function c(e){let t=l(e),n=t.value;switch(t.type){case"integer":return Number.isSafeInteger(n)?{type:"integer",value:n}:s();case"decimal":return typeof n=="string"&&/^(?:0|-?[1-9]\d*)(?:\.\d*[1-9])?$|^-0\.\d*[1-9]$/.test(n)?{type:"decimal",value:n}:s();case"boolean":return typeof n=="boolean"?{type:"boolean",value:n}:s();case"string":return typeof n=="string"?{type:"string",value:n}:s();case"date":return typeof n=="string"&&/^\d{4}-\d{2}-\d{2}$/.test(n)&&!Number.isNaN(Date.parse(n))&&new Date(n).toISOString().slice(0,10)===n?{type:"date",value:n}:s();case"enum":{let r=t.allowedValues;return!Array.isArray(r)||!r.length||!r.every(o=>typeof o=="string"&&o!=="")||new Set(r).size!==r.length||!r.includes(n)?s():{type:"enum",value:a(n),allowedValues:[...r].sort()}}case"reference":{let r=l(n);return{type:"reference",value:{documentId:a(r.documentId),documentRevision:a(r.documentRevision)}}}default:return s()}}function b(e){return e!=="identity/v1"&&e!=="floorHundredsPlus/v1"?s():e}function v(e,t){let n=c(e);return b(t)==="floorHundredsPlus/v1"?n.type!=="integer"||n.value<0?s():n.value<100?String(n.value):`${(Math.floor(n.value/100)*100).toLocaleString("en-GB")}+`:n.type==="reference"?n.value.documentId:String(n.value)}function w(e,t,n){return["htmlText","htmlAttribute","json"].includes(n)?e.replace(/\{([^{}]*)\}/g,(r,o)=>{d(o);let u=Object.hasOwn(g,o)?g[o]:o;if(!Object.hasOwn(t,u))throw new Error(`Unknown fact placeholder: ${o}`);let i=t[u];return n==="json"?JSON.stringify(i).slice(1,-1).replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029"):i.replace(/[&<>"']/g,f=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[f])}):s()}function p(e){return e===null||typeof e=="boolean"||typeof e=="string"||typeof e=="number"&&Number.isFinite(e)?JSON.stringify(e):Array.isArray(e)?`[${Array.from(e,p).join(",")}]`:typeof e!="object"||e===null||Object.getPrototypeOf(e)!==Object.prototype?s():`{${Object.entries(e).sort(([t],[n])=>t<n?-1:t>n?1:0).map(([t,n])=>`${JSON.stringify(t)}:${p(n)}`).join(",")}}`}async function h(e){let t=e.fact;d(t.key);let n=c(t.publicValue);v(n,t.displayRule),(!Number.isSafeInteger(e.revision)||e.revision<1||e.baseRevision!==null&&(!Number.isSafeInteger(e.baseRevision)||e.baseRevision<1||e.baseRevision>=e.revision))&&s(),a(e.projectId),a(t.accountableOwner),a(t.source.definition),a(t.source.version),(!["computed","observed","attested"].includes(t.source.kind)||!Object.hasOwn(m,t.freshness)||typeof t.optional!="boolean"||t.unit!==null&&typeof t.unit!="string")&&s(),t.timezone!==null&&new Intl.DateTimeFormat("en-GB",{timeZone:a(t.timezone)});let r=i=>((!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(i)||!Number.isFinite(Date.parse(i))||new Date(i).toISOString()!==i.replace(/(?<=:\d{2})Z$/,".000Z"))&&s(),Date.parse(i));(r(e.expiresAt)<=r(e.effectiveAt)||r(e.evidence.expiresAt)<=r(e.evidence.observedAt)||!/^[0-9a-f]{64}$/.test(e.evidence.digest)||typeof e.evidence.complete!="boolean")&&s(),a(e.evidence.reference),a(e.evidence.runId);for(let i of t.surfaces)a(i.id),i.semantics==="historical"?c({type:"date",value:i.asOf}):(i.semantics!=="current"||i.asOf!==null)&&s();let o={projectId:e.projectId,revision:e.revision,baseRevision:e.baseRevision,fact:{key:t.key,publicValue:n,unit:t.unit,timezone:t.timezone,displayRule:t.displayRule,source:{kind:t.source.kind,definition:t.source.definition,version:t.source.version},accountableOwner:t.accountableOwner,freshness:t.freshness,optional:t.optional,surfaces:t.surfaces.map(i=>({id:i.id,semantics:i.semantics,asOf:i.asOf}))},evidence:{reference:e.evidence.reference,digest:e.evidence.digest,complete:e.evidence.complete,runId:e.evidence.runId,observedAt:new Date(r(e.evidence.observedAt)).toISOString(),expiresAt:new Date(r(e.evidence.expiresAt)).toISOString()},effectiveAt:new Date(r(e.effectiveAt)).toISOString(),expiresAt:new Date(r(e.expiresAt)).toISOString()},u=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(p(o)));return Array.from(new Uint8Array(u),i=>i.toString(16).padStart(2,"0")).join("")}export{g as FACT_ALIASES,x as FACT_DISPLAY_RULES,m as FACT_EXPIRY_MS,y as FACT_KEY,F as FACT_TYPES,d as assertFactKey,S as businessFactId,p as canonicalFactJson,w as expandFactPlaceholders,v as formatFact,h as hashFactRevision,b as parseFactDisplayRule,c as parseFactValue,R as parseStoredFactValue,A as storeFactValue};
2
+ //# sourceMappingURL=facts.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/contracts/facts.ts"],
4
+ "sourcesContent": ["/** Public contracts only. Evidence and approval identities never enter the CMS document. */\nexport const FACT_TYPES = [\"integer\", \"decimal\", \"boolean\", \"date\", \"string\", \"enum\", \"reference\"] as const;\nexport const FACT_DISPLAY_RULES = [\"identity/v1\", \"floorHundredsPlus/v1\"] as const;\nexport type FactDisplayRule = typeof FACT_DISPLAY_RULES[number];\nexport const FACT_KEY = /^[a-z][a-zA-Z0-9]*(?:\\.[a-zA-Z][a-zA-Z0-9]*)*$/;\nexport const FACT_ALIASES: Readonly<Record<string, string>> = { pubCount: \"coverage.pubCount\" };\nexport type FactValue =\n | { type: \"integer\"; value: number }\n // Decimal strings avoid binary floating-point loss; canonical form has no trailing fractional zeroes.\n | { type: \"decimal\"; value: string }\n | { type: \"boolean\"; value: boolean }\n | { type: \"date\" | \"string\"; value: string }\n | { type: \"enum\"; value: string; allowedValues: readonly string[] }\n | { type: \"reference\"; value: { documentId: string; documentRevision: string } };\nexport type FactSource = { kind: \"computed\" | \"observed\" | \"attested\"; definition: string; version: string };\nexport type BusinessFact = {\n key: string; publicValue: FactValue; unit: string | null; timezone: string | null;\n displayRule: FactDisplayRule; source: FactSource; accountableOwner: string;\n freshness: \"computed\" | \"operational\" | \"technical\"; optional: boolean;\n surfaces: readonly { id: string; semantics: \"current\" | \"historical\"; asOf: string | null }[];\n};\nexport const FACT_EXPIRY_MS = { computed: 72 * 3_600_000, operational: 30 * 86_400_000, technical: 90 * 86_400_000 } as const;\nexport type FactEvidence = { reference: string; digest: string; observedAt: string; expiresAt: string; complete: boolean; runId: string };\nexport type FactRevision = {\n projectId: string; revision: number; baseRevision: number | null; fact: BusinessFact;\n evidence: FactEvidence; effectiveAt: string; expiresAt: string;\n};\nexport type ApprovedFactRevision = FactRevision & {\n hash: string; approvedAt: string;\n approval: { kind: \"signer\"; signerId: string } | { kind: \"policy\"; policyVersion: string };\n};\nexport type FactPublicationReceipt = {\n intentId: string; projectId: string; revision: number; hash: string;\n destination: { projectId: string; dataset: string }; expectedDocumentRevision: string | null;\n cms: { transactionId: string; documentRevision: string; readBackHash: string; at: string } | null;\n invalidation: { deliveryKey: string; target: string; type: \"businessFact\"; id: string; at: string } | null;\n render: { url: string; deploymentId: string; revision: number; hash: string; at: string } | null;\n};\nexport type PublicBusinessFact = Pick<BusinessFact, \"key\" | \"publicValue\" | \"unit\" | \"timezone\" | \"displayRule\"> & {\n _type: \"businessFact\"; _id: string; approvedRevision: number; approvedHash: string;\n effectiveAt: string; expiresAt: string; provenanceSummary: string;\n};\n/** A request supplies one snapshot to every surface. Missing and unavailable are distinct from an empty snapshot. */\nexport type FactSnapshot = Readonly<Record<string, PublicBusinessFact>>;\nexport type FactReadResult = { status: \"available\"; snapshot: FactSnapshot } | { status: \"unavailable\"; reason: string };\nexport type FactReader = () => Promise<FactReadResult>;\nexport type FactPlaceholderContext = \"htmlText\" | \"htmlAttribute\" | \"json\";\n/** Stored Sanity projection: scalar unions use named slots, never private revision fields. */\nexport type StoredFactValue = { type: FactValue[\"type\"]; numberValue?: number; textValue?: string;\n booleanValue?: boolean; allowedValues?: readonly string[]; documentId?: string; documentRevision?: string };\nexport function parseStoredFactValue(input: unknown): FactValue {\n const entry = record(input);\n const value = entry.type === \"integer\" ? entry.numberValue : entry.type === \"boolean\" ? entry.booleanValue\n : entry.type === \"reference\" ? { documentId: entry.documentId, documentRevision: entry.documentRevision } : entry.textValue;\n return parseFactValue({ type: entry.type, value, allowedValues: entry.allowedValues });\n}\nexport function storeFactValue(input: FactValue): StoredFactValue {\n const entry = parseFactValue(input);\n if (entry.type === \"integer\") return { type: entry.type, numberValue: entry.value };\n if (entry.type === \"boolean\") return { type: entry.type, booleanValue: entry.value };\n if (entry.type === \"reference\") return { type: entry.type, ...entry.value };\n return entry.type === \"enum\" ? { type: entry.type, textValue: entry.value, allowedValues: entry.allowedValues }\n : { type: entry.type, textValue: entry.value };\n}\nexport type StoredBusinessFact = Omit<PublicBusinessFact, \"publicValue\"> & { publicValue: StoredFactValue };\n\nfunction invalid(): never { throw new Error(\"Invalid business fact contract\"); }\nfunction record(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return invalid();\n return value as Record<string, unknown>;\n}\nfunction text(value: unknown): string { return typeof value === \"string\" && value.trim() !== \"\" ? value : invalid(); }\nexport function assertFactKey(key: string): void {\n if (key.length > 48 || !FACT_KEY.test(key)) invalid();\n}\nexport function businessFactId(key: string): string {\n assertFactKey(key);\n // Dataset-local, injective encoding; never a draft ID or a key containing a path separator.\n return \"businessFact-\" + Array.from(new TextEncoder().encode(key), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\nexport function parseFactValue(input: unknown): FactValue {\n const entry = record(input), value = entry.value;\n switch (entry.type) {\n case \"integer\": return Number.isSafeInteger(value) ? { type: \"integer\", value: value as number } : invalid();\n case \"decimal\": return typeof value === \"string\" && /^(?:0|-?[1-9]\\d*)(?:\\.\\d*[1-9])?$|^-0\\.\\d*[1-9]$/.test(value)\n ? { type: \"decimal\", value } : invalid();\n case \"boolean\": return typeof value === \"boolean\" ? { type: \"boolean\", value } : invalid();\n case \"string\": return typeof value === \"string\" ? { type: \"string\", value } : invalid();\n case \"date\": return typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)\n && !Number.isNaN(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value ? { type: \"date\", value } : invalid();\n case \"enum\": {\n const allowed = entry.allowedValues;\n if (!Array.isArray(allowed) || !allowed.length || !allowed.every((item) => typeof item === \"string\" && item !== \"\")\n || new Set(allowed).size !== allowed.length || !allowed.includes(value)) return invalid();\n return { type: \"enum\", value: text(value), allowedValues: [...allowed].sort() as string[] };\n }\n case \"reference\": {\n const ref = record(value);\n return { type: \"reference\", value: { documentId: text(ref.documentId), documentRevision: text(ref.documentRevision) } };\n }\n default: return invalid();\n }\n}\nexport function parseFactDisplayRule(input: unknown): FactDisplayRule {\n if (input !== \"identity/v1\" && input !== \"floorHundredsPlus/v1\") return invalid();\n return input;\n}\nexport function formatFact(input: FactValue, rule: FactDisplayRule): string {\n const value = parseFactValue(input);\n if (parseFactDisplayRule(rule) === \"floorHundredsPlus/v1\") {\n if (value.type !== \"integer\" || value.value < 0) return invalid();\n return value.value < 100 ? String(value.value) : `${(Math.floor(value.value / 100) * 100).toLocaleString(\"en-GB\")}+`;\n }\n return value.type === \"reference\" ? value.value.documentId : String(value.value);\n}\n/** Escape each replacement, not the surrounding trusted template. JSON returns string content without quotes;\n * callers serialize complete structured objects and keep numeric properties as exact values. Attributes must be quoted.\n * Raw text/React children use the formatter directly; never HTML-unescape an expanded string.\n */\nexport function expandFactPlaceholders(template: string, values: Readonly<Record<string, string>>, context: FactPlaceholderContext): string {\n if (![\"htmlText\", \"htmlAttribute\", \"json\"].includes(context)) return invalid();\n return template.replace(/\\{([^{}]*)\\}/g, (_match, key: string) => {\n assertFactKey(key);\n const resolved = Object.hasOwn(FACT_ALIASES, key) ? FACT_ALIASES[key]! : key;\n if (!Object.hasOwn(values, resolved)) throw new Error(`Unknown fact placeholder: ${key}`);\n const value = values[resolved]!;\n if (context === \"json\") return JSON.stringify(value).slice(1, -1)\n .replace(/</g, \"\\\\u003c\").replace(/>/g, \"\\\\u003e\").replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\").replace(/\\u2029/g, \"\\\\u2029\");\n return value.replace(/[&<>\"']/g, (char) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[char]!);\n });\n}\n/** Fixed JSON domain, sorted object keys, array order preserved, finite numbers only. */\nexport function canonicalFactJson(value: unknown): string {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") return JSON.stringify(value);\n if (typeof value === \"number\" && Number.isFinite(value)) return JSON.stringify(value);\n if (Array.isArray(value)) return `[${Array.from(value, canonicalFactJson).join(\",\")}]`;\n if (typeof value !== \"object\" || value === null || Object.getPrototypeOf(value) !== Object.prototype) return invalid();\n return `{${Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)\n .map(([key, item]) => `${JSON.stringify(key)}:${canonicalFactJson(item)}`).join(\",\")}}`;\n}\n/** SHA-256 of contract fields only; unknown fields and approval are ignored. Instants use UTC milliseconds. */\nexport async function hashFactRevision(revision: FactRevision): Promise<string> {\n const fact = revision.fact;\n assertFactKey(fact.key);\n const publicValue = parseFactValue(fact.publicValue);\n formatFact(publicValue, fact.displayRule);\n if (!Number.isSafeInteger(revision.revision) || revision.revision < 1\n || (revision.baseRevision !== null && (!Number.isSafeInteger(revision.baseRevision) || revision.baseRevision < 1 || revision.baseRevision >= revision.revision))) invalid();\n text(revision.projectId); text(fact.accountableOwner); text(fact.source.definition); text(fact.source.version);\n if (![\"computed\", \"observed\", \"attested\"].includes(fact.source.kind) || !Object.hasOwn(FACT_EXPIRY_MS, fact.freshness)\n || typeof fact.optional !== \"boolean\" || (fact.unit !== null && typeof fact.unit !== \"string\")) invalid();\n if (fact.timezone !== null) new Intl.DateTimeFormat(\"en-GB\", { timeZone: text(fact.timezone) });\n const instant = (value: string) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/.test(value) || !Number.isFinite(Date.parse(value))\n || new Date(value).toISOString() !== value.replace(/(?<=:\\d{2})Z$/, \".000Z\")) invalid();\n return Date.parse(value);\n };\n if (instant(revision.expiresAt) <= instant(revision.effectiveAt)\n || instant(revision.evidence.expiresAt) <= instant(revision.evidence.observedAt)\n || !/^[0-9a-f]{64}$/.test(revision.evidence.digest) || typeof revision.evidence.complete !== \"boolean\") invalid();\n text(revision.evidence.reference); text(revision.evidence.runId);\n for (const surface of fact.surfaces) {\n text(surface.id);\n if (surface.semantics === \"historical\") parseFactValue({ type: \"date\", value: surface.asOf });\n else if (surface.semantics !== \"current\" || surface.asOf !== null) invalid();\n }\n const candidate: FactRevision = {\n projectId: revision.projectId, revision: revision.revision, baseRevision: revision.baseRevision,\n fact: {\n key: fact.key, publicValue, unit: fact.unit, timezone: fact.timezone, displayRule: fact.displayRule,\n source: { kind: fact.source.kind, definition: fact.source.definition, version: fact.source.version },\n accountableOwner: fact.accountableOwner, freshness: fact.freshness, optional: fact.optional,\n surfaces: fact.surfaces.map((surface) => ({ id: surface.id, semantics: surface.semantics, asOf: surface.asOf })),\n },\n evidence: {\n reference: revision.evidence.reference, digest: revision.evidence.digest, complete: revision.evidence.complete,\n runId: revision.evidence.runId, observedAt: new Date(instant(revision.evidence.observedAt)).toISOString(),\n expiresAt: new Date(instant(revision.evidence.expiresAt)).toISOString(),\n },\n effectiveAt: new Date(instant(revision.effectiveAt)).toISOString(),\n expiresAt: new Date(instant(revision.expiresAt)).toISOString(),\n };\n const digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(canonicalFactJson(candidate)));\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],
5
+ "mappings": "AACO,IAAMA,EAAa,CAAC,UAAW,UAAW,UAAW,OAAQ,SAAU,OAAQ,WAAW,EACpFC,EAAqB,CAAC,cAAe,sBAAsB,EAE3DC,EAAW,iDACXC,EAAiD,CAAE,SAAU,mBAAoB,EAgBjFC,EAAiB,CAAE,SAAU,GAAK,KAAW,YAAa,GAAK,MAAY,UAAW,GAAK,KAAW,EA6B5G,SAASC,EAAqBC,EAA2B,CAC9D,IAAMC,EAAQC,EAAOF,CAAK,EACpBG,EAAQF,EAAM,OAAS,UAAYA,EAAM,YAAcA,EAAM,OAAS,UAAYA,EAAM,aAC1FA,EAAM,OAAS,YAAc,CAAE,WAAYA,EAAM,WAAY,iBAAkBA,EAAM,gBAAiB,EAAIA,EAAM,UACpH,OAAOG,EAAe,CAAE,KAAMH,EAAM,KAAM,MAAAE,EAAO,cAAeF,EAAM,aAAc,CAAC,CACvF,CACO,SAASI,EAAeL,EAAmC,CAChE,IAAMC,EAAQG,EAAeJ,CAAK,EAClC,OAAIC,EAAM,OAAS,UAAkB,CAAE,KAAMA,EAAM,KAAM,YAAaA,EAAM,KAAM,EAC9EA,EAAM,OAAS,UAAkB,CAAE,KAAMA,EAAM,KAAM,aAAcA,EAAM,KAAM,EAC/EA,EAAM,OAAS,YAAoB,CAAE,KAAMA,EAAM,KAAM,GAAGA,EAAM,KAAM,EACnEA,EAAM,OAAS,OAAS,CAAE,KAAMA,EAAM,KAAM,UAAWA,EAAM,MAAO,cAAeA,EAAM,aAAc,EAC1G,CAAE,KAAMA,EAAM,KAAM,UAAWA,EAAM,KAAM,CACjD,CAGA,SAASK,GAAiB,CAAE,MAAM,IAAI,MAAM,gCAAgC,CAAG,CAC/E,SAASJ,EAAOC,EAAyC,CACvD,MAAI,CAACA,GAAS,OAAOA,GAAU,UAAY,MAAM,QAAQA,CAAK,EAAUG,EAAQ,EACzEH,CACT,CACA,SAASI,EAAKJ,EAAwB,CAAE,OAAO,OAAOA,GAAU,UAAYA,EAAM,KAAK,IAAM,GAAKA,EAAQG,EAAQ,CAAG,CAC9G,SAASE,EAAcC,EAAmB,EAC3CA,EAAI,OAAS,IAAM,CAACb,EAAS,KAAKa,CAAG,IAAGH,EAAQ,CACtD,CACO,SAASI,EAAeD,EAAqB,CAClD,OAAAD,EAAcC,CAAG,EAEV,gBAAkB,MAAM,KAAK,IAAI,YAAY,EAAE,OAAOA,CAAG,EAAIE,GAASA,EAAK,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAC1H,CACO,SAASP,EAAeJ,EAA2B,CACxD,IAAMC,EAAQC,EAAOF,CAAK,EAAGG,EAAQF,EAAM,MAC3C,OAAQA,EAAM,KAAM,CAClB,IAAK,UAAW,OAAO,OAAO,cAAcE,CAAK,EAAI,CAAE,KAAM,UAAW,MAAOA,CAAgB,EAAIG,EAAQ,EAC3G,IAAK,UAAW,OAAO,OAAOH,GAAU,UAAY,mDAAmD,KAAKA,CAAK,EAC7G,CAAE,KAAM,UAAW,MAAAA,CAAM,EAAIG,EAAQ,EACzC,IAAK,UAAW,OAAO,OAAOH,GAAU,UAAY,CAAE,KAAM,UAAW,MAAAA,CAAM,EAAIG,EAAQ,EACzF,IAAK,SAAU,OAAO,OAAOH,GAAU,SAAW,CAAE,KAAM,SAAU,MAAAA,CAAM,EAAIG,EAAQ,EACtF,IAAK,OAAQ,OAAO,OAAOH,GAAU,UAAY,sBAAsB,KAAKA,CAAK,GAC5E,CAAC,OAAO,MAAM,KAAK,MAAMA,CAAK,CAAC,GAAK,IAAI,KAAKA,CAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,IAAMA,EAAQ,CAAE,KAAM,OAAQ,MAAAA,CAAM,EAAIG,EAAQ,EAClI,IAAK,OAAQ,CACX,IAAMM,EAAUX,EAAM,cACtB,MAAI,CAAC,MAAM,QAAQW,CAAO,GAAK,CAACA,EAAQ,QAAU,CAACA,EAAQ,MAAOC,GAAS,OAAOA,GAAS,UAAYA,IAAS,EAAE,GAC7G,IAAI,IAAID,CAAO,EAAE,OAASA,EAAQ,QAAU,CAACA,EAAQ,SAAST,CAAK,EAAUG,EAAQ,EACnF,CAAE,KAAM,OAAQ,MAAOC,EAAKJ,CAAK,EAAG,cAAe,CAAC,GAAGS,CAAO,EAAE,KAAK,CAAc,CAC5F,CACA,IAAK,YAAa,CAChB,IAAME,EAAMZ,EAAOC,CAAK,EACxB,MAAO,CAAE,KAAM,YAAa,MAAO,CAAE,WAAYI,EAAKO,EAAI,UAAU,EAAG,iBAAkBP,EAAKO,EAAI,gBAAgB,CAAE,CAAE,CACxH,CACA,QAAS,OAAOR,EAAQ,CAC1B,CACF,CACO,SAASS,EAAqBf,EAAiC,CACpE,OAAIA,IAAU,eAAiBA,IAAU,uBAA+BM,EAAQ,EACzEN,CACT,CACO,SAASgB,EAAWhB,EAAkBiB,EAA+B,CAC1E,IAAMd,EAAQC,EAAeJ,CAAK,EAClC,OAAIe,EAAqBE,CAAI,IAAM,uBAC7Bd,EAAM,OAAS,WAAaA,EAAM,MAAQ,EAAUG,EAAQ,EACzDH,EAAM,MAAQ,IAAM,OAAOA,EAAM,KAAK,EAAI,IAAI,KAAK,MAAMA,EAAM,MAAQ,GAAG,EAAI,KAAK,eAAe,OAAO,CAAC,IAE5GA,EAAM,OAAS,YAAcA,EAAM,MAAM,WAAa,OAAOA,EAAM,KAAK,CACjF,CAKO,SAASe,EAAuBC,EAAkBC,EAA0CC,EAAyC,CAC1I,MAAK,CAAC,WAAY,gBAAiB,MAAM,EAAE,SAASA,CAAO,EACpDF,EAAS,QAAQ,gBAAiB,CAACG,EAAQb,IAAgB,CAChED,EAAcC,CAAG,EACjB,IAAMc,EAAW,OAAO,OAAO1B,EAAcY,CAAG,EAAIZ,EAAaY,CAAG,EAAKA,EACzE,GAAI,CAAC,OAAO,OAAOW,EAAQG,CAAQ,EAAG,MAAM,IAAI,MAAM,6BAA6Bd,CAAG,EAAE,EACxF,IAAMN,EAAQiB,EAAOG,CAAQ,EAC7B,OAAIF,IAAY,OAAe,KAAK,UAAUlB,CAAK,EAAE,MAAM,EAAG,EAAE,EAC7D,QAAQ,KAAM,SAAS,EAAE,QAAQ,KAAM,SAAS,EAAE,QAAQ,KAAM,SAAS,EACzE,QAAQ,UAAW,SAAS,EAAE,QAAQ,UAAW,SAAS,EACtDA,EAAM,QAAQ,WAAaqB,IAAU,CAAE,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,OAAQ,GAAGA,CAAI,CAAE,CAC7H,CAAC,EAVoElB,EAAQ,CAW/E,CAEO,SAASmB,EAAkBtB,EAAwB,CAExD,OADIA,IAAU,MAAQ,OAAOA,GAAU,WAAa,OAAOA,GAAU,UACjE,OAAOA,GAAU,UAAY,OAAO,SAASA,CAAK,EAAU,KAAK,UAAUA,CAAK,EAChF,MAAM,QAAQA,CAAK,EAAU,IAAI,MAAM,KAAKA,EAAOsB,CAAiB,EAAE,KAAK,GAAG,CAAC,IAC/E,OAAOtB,GAAU,UAAYA,IAAU,MAAQ,OAAO,eAAeA,CAAK,IAAM,OAAO,UAAkBG,EAAQ,EAC9G,IAAI,OAAO,QAAQH,CAAK,EAAE,KAAK,CAAC,CAACuB,CAAC,EAAG,CAACC,CAAC,IAAMD,EAAIC,EAAI,GAAKD,EAAIC,EAAI,EAAI,CAAC,EAC3E,IAAI,CAAC,CAAClB,EAAKI,CAAI,IAAM,GAAG,KAAK,UAAUJ,CAAG,CAAC,IAAIgB,EAAkBZ,CAAI,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC,GACxF,CAEA,eAAsBe,EAAiBC,EAAyC,CAC9E,IAAMC,EAAOD,EAAS,KACtBrB,EAAcsB,EAAK,GAAG,EACtB,IAAMC,EAAc3B,EAAe0B,EAAK,WAAW,EACnDd,EAAWe,EAAaD,EAAK,WAAW,GACpC,CAAC,OAAO,cAAcD,EAAS,QAAQ,GAAKA,EAAS,SAAW,GAC9DA,EAAS,eAAiB,OAAS,CAAC,OAAO,cAAcA,EAAS,YAAY,GAAKA,EAAS,aAAe,GAAKA,EAAS,cAAgBA,EAAS,YAAYvB,EAAQ,EAC5KC,EAAKsB,EAAS,SAAS,EAAGtB,EAAKuB,EAAK,gBAAgB,EAAGvB,EAAKuB,EAAK,OAAO,UAAU,EAAGvB,EAAKuB,EAAK,OAAO,OAAO,GACzG,CAAC,CAAC,WAAY,WAAY,UAAU,EAAE,SAASA,EAAK,OAAO,IAAI,GAAK,CAAC,OAAO,OAAOhC,EAAgBgC,EAAK,SAAS,GAChH,OAAOA,EAAK,UAAa,WAAcA,EAAK,OAAS,MAAQ,OAAOA,EAAK,MAAS,WAAWxB,EAAQ,EACtGwB,EAAK,WAAa,MAAM,IAAI,KAAK,eAAe,QAAS,CAAE,SAAUvB,EAAKuB,EAAK,QAAQ,CAAE,CAAC,EAC9F,IAAME,EAAW7B,KACX,CAAC,qDAAqD,KAAKA,CAAK,GAAK,CAAC,OAAO,SAAS,KAAK,MAAMA,CAAK,CAAC,GACtG,IAAI,KAAKA,CAAK,EAAE,YAAY,IAAMA,EAAM,QAAQ,gBAAiB,OAAO,IAAGG,EAAQ,EACjF,KAAK,MAAMH,CAAK,IAErB6B,EAAQH,EAAS,SAAS,GAAKG,EAAQH,EAAS,WAAW,GAC1DG,EAAQH,EAAS,SAAS,SAAS,GAAKG,EAAQH,EAAS,SAAS,UAAU,GAC5E,CAAC,iBAAiB,KAAKA,EAAS,SAAS,MAAM,GAAK,OAAOA,EAAS,SAAS,UAAa,YAAWvB,EAAQ,EAClHC,EAAKsB,EAAS,SAAS,SAAS,EAAGtB,EAAKsB,EAAS,SAAS,KAAK,EAC/D,QAAWI,KAAWH,EAAK,SACzBvB,EAAK0B,EAAQ,EAAE,EACXA,EAAQ,YAAc,aAAc7B,EAAe,CAAE,KAAM,OAAQ,MAAO6B,EAAQ,IAAK,CAAC,GACnFA,EAAQ,YAAc,WAAaA,EAAQ,OAAS,OAAM3B,EAAQ,EAE7E,IAAM4B,EAA0B,CAC9B,UAAWL,EAAS,UAAW,SAAUA,EAAS,SAAU,aAAcA,EAAS,aACnF,KAAM,CACJ,IAAKC,EAAK,IAAK,YAAAC,EAAa,KAAMD,EAAK,KAAM,SAAUA,EAAK,SAAU,YAAaA,EAAK,YACxF,OAAQ,CAAE,KAAMA,EAAK,OAAO,KAAM,WAAYA,EAAK,OAAO,WAAY,QAASA,EAAK,OAAO,OAAQ,EACnG,iBAAkBA,EAAK,iBAAkB,UAAWA,EAAK,UAAW,SAAUA,EAAK,SACnF,SAAUA,EAAK,SAAS,IAAKG,IAAa,CAAE,GAAIA,EAAQ,GAAI,UAAWA,EAAQ,UAAW,KAAMA,EAAQ,IAAK,EAAE,CACjH,EACA,SAAU,CACR,UAAWJ,EAAS,SAAS,UAAW,OAAQA,EAAS,SAAS,OAAQ,SAAUA,EAAS,SAAS,SACtG,MAAOA,EAAS,SAAS,MAAO,WAAY,IAAI,KAAKG,EAAQH,EAAS,SAAS,UAAU,CAAC,EAAE,YAAY,EACxG,UAAW,IAAI,KAAKG,EAAQH,EAAS,SAAS,SAAS,CAAC,EAAE,YAAY,CACxE,EACA,YAAa,IAAI,KAAKG,EAAQH,EAAS,WAAW,CAAC,EAAE,YAAY,EACjE,UAAW,IAAI,KAAKG,EAAQH,EAAS,SAAS,CAAC,EAAE,YAAY,CAC/D,EACMM,EAAS,MAAM,OAAO,OAAO,OAAO,UAAW,IAAI,YAAY,EAAE,OAAOV,EAAkBS,CAAS,CAAC,CAAC,EAC3G,OAAO,MAAM,KAAK,IAAI,WAAWC,CAAM,EAAIxB,GAASA,EAAK,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CACjG",
6
+ "names": ["FACT_TYPES", "FACT_DISPLAY_RULES", "FACT_KEY", "FACT_ALIASES", "FACT_EXPIRY_MS", "parseStoredFactValue", "input", "entry", "record", "value", "parseFactValue", "storeFactValue", "invalid", "text", "assertFactKey", "key", "businessFactId", "byte", "allowed", "item", "ref", "parseFactDisplayRule", "formatFact", "rule", "expandFactPlaceholders", "template", "values", "context", "_match", "resolved", "char", "canonicalFactJson", "a", "b", "hashFactRevision", "revision", "fact", "publicValue", "instant", "surface", "candidate", "digest"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weatherboard/cms",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "Published-content contracts, server readers, adapters and portable content tooling.",
6
6
  "license": "MIT",
@@ -93,6 +93,26 @@
93
93
  "types": "./dist/guard.d.cts",
94
94
  "default": "./dist/guard.cjs"
95
95
  }
96
+ },
97
+ "./facts": {
98
+ "import": {
99
+ "types": "./dist/facts.d.mts",
100
+ "default": "./dist/facts.mjs"
101
+ },
102
+ "require": {
103
+ "types": "./dist/facts.d.cts",
104
+ "default": "./dist/facts.cjs"
105
+ }
106
+ },
107
+ "./fact-guard": {
108
+ "import": {
109
+ "types": "./dist/fact-guard.d.mts",
110
+ "default": "./dist/fact-guard.mjs"
111
+ },
112
+ "require": {
113
+ "types": "./dist/fact-guard.d.cts",
114
+ "default": "./dist/fact-guard.cjs"
115
+ }
96
116
  }
97
117
  },
98
118
  "main": "./dist/index.cjs",
@@ -109,7 +129,9 @@
109
129
  "next": 16384,
110
130
  "sanity": 16384,
111
131
  "webhook": 16384,
112
- "site-content": 32768
132
+ "site-content": 32768,
133
+ "facts": 8192,
134
+ "fact-guard": 8192
113
135
  },
114
136
  "dependencies": {
115
137
  "server-only": "0.0.1"