@weatherboard/cms 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/README.md +11 -5
- package/dist/contracts/facts.d.cts +163 -0
- package/dist/contracts/facts.d.mts +163 -0
- package/dist/contracts/facts.d.ts +163 -0
- package/dist/fact-guard.cjs +2 -0
- package/dist/fact-guard.cjs.map +7 -0
- package/dist/fact-guard.d.cts +19 -0
- package/dist/fact-guard.d.mts +19 -0
- package/dist/fact-guard.d.ts +19 -0
- package/dist/fact-guard.mjs +2 -0
- package/dist/fact-guard.mjs.map +7 -0
- package/dist/facts.cjs +2 -0
- package/dist/facts.cjs.map +7 -0
- package/dist/facts.d.cts +1 -0
- package/dist/facts.d.mts +1 -0
- package/dist/facts.d.ts +1 -0
- package/dist/facts.mjs +2 -0
- package/dist/facts.mjs.map +7 -0
- package/dist/next.cjs +4 -4
- package/dist/next.cjs.map +3 -3
- package/dist/next.mjs +4 -4
- package/dist/next.mjs.map +3 -3
- package/dist/runtime/site-readers.d.cts +7 -1
- package/dist/runtime/site-readers.d.mts +7 -1
- package/dist/runtime/site-readers.d.ts +7 -1
- package/dist/runtime/test-fixtures/saved-documents.d.cts +4 -0
- package/dist/runtime/test-fixtures/saved-documents.d.mts +4 -0
- package/dist/runtime/test-fixtures/saved-documents.d.ts +4 -0
- package/dist/server.cjs +3 -3
- package/dist/server.cjs.map +3 -3
- package/dist/server.mjs +2 -2
- package/dist/server.mjs.map +3 -3
- package/package.json +24 -2
package/README.md
CHANGED
|
@@ -42,10 +42,14 @@ when names match. The host keeps its repository data and chooses how to display
|
|
|
42
42
|
the source. `menuQuery`, `parseMenuDocument` and `menuTags` remain the menu API.
|
|
43
43
|
|
|
44
44
|
`openVacanciesQuery`, `parseVacancies` and `parseVacancyDocument` require the
|
|
45
|
-
consumer's validator
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`location`
|
|
45
|
+
consumer's validator, which receives each raw projected entry before display
|
|
46
|
+
fields are derived. The query resolves `workLocation` to `{ _id, name }` and
|
|
47
|
+
depends on both vacancy and location tags. The display label uses the resolved
|
|
48
|
+
name, then a non-`Other` legacy `location`, then `locationNote`; a dangling
|
|
49
|
+
reference falls through to the legacy fields. `locationId` is the resolved
|
|
50
|
+
location's ID or `null`. An entry with no display label is structurally dropped.
|
|
51
|
+
Legacy documents remain readable. Consumers whose validators require the legacy
|
|
52
|
+
string must update them before migrating documents to references.
|
|
49
53
|
|
|
50
54
|
## Site hero
|
|
51
55
|
|
|
@@ -240,9 +244,11 @@ from a later caller read. The returned objects share no caller-owned data.
|
|
|
240
244
|
CMS has no runtime dependency on the unpublished menu or schema packages.
|
|
241
245
|
Compatibility is exercised with a copied, neutral café saved-shape fixture;
|
|
242
246
|
there is no schema devDependency or menu test-fixture import. Publication tests
|
|
243
|
-
resolve imports in source, all
|
|
247
|
+
resolve imports in source, all ten entry bundles and their build input paths,
|
|
244
248
|
including relative imports into either forbidden package root and type specifiers.
|
|
245
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).
|
|
246
252
|
|
|
247
253
|
```ts
|
|
248
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,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=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[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
|