@kashscript/lexicons 0.1.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/LICENSE +199 -0
- package/README.md +73 -0
- package/dist/engine.d.ts +48 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +110 -0
- package/dist/engine.js.map +1 -0
- package/dist/envelope.d.ts +59 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +43 -0
- package/dist/envelope.js.map +1 -0
- package/dist/extensions/social.d.ts +82 -0
- package/dist/extensions/social.d.ts.map +1 -0
- package/dist/extensions/social.js +99 -0
- package/dist/extensions/social.js.map +1 -0
- package/dist/extensions/trade.d.ts +94 -0
- package/dist/extensions/trade.d.ts.map +1 -0
- package/dist/extensions/trade.js +116 -0
- package/dist/extensions/trade.js.map +1 -0
- package/dist/extensions/zkp.d.ts +76 -0
- package/dist/extensions/zkp.d.ts.map +1 -0
- package/dist/extensions/zkp.js +68 -0
- package/dist/extensions/zkp.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/json-schema.d.ts +18 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +125 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/lexicons/index.d.ts +32 -0
- package/dist/lexicons/index.d.ts.map +1 -0
- package/dist/lexicons/index.js +59 -0
- package/dist/lexicons/index.js.map +1 -0
- package/dist/lexicons/kash-id-audit.d.ts +32 -0
- package/dist/lexicons/kash-id-audit.d.ts.map +1 -0
- package/dist/lexicons/kash-id-audit.js +21 -0
- package/dist/lexicons/kash-id-audit.js.map +1 -0
- package/dist/lexicons/kash-id-preferences.d.ts +60 -0
- package/dist/lexicons/kash-id-preferences.d.ts.map +1 -0
- package/dist/lexicons/kash-id-preferences.js +43 -0
- package/dist/lexicons/kash-id-preferences.js.map +1 -0
- package/dist/lexicons/kash-id-profile.d.ts +46 -0
- package/dist/lexicons/kash-id-profile.d.ts.map +1 -0
- package/dist/lexicons/kash-id-profile.js +31 -0
- package/dist/lexicons/kash-id-profile.js.map +1 -0
- package/dist/registry.d.ts +78 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +133 -0
- package/dist/registry.js.map +1 -0
- package/dist/schema.d.ts +101 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +224 -0
- package/dist/schema.js.map +1 -0
- package/dist/semver.d.ts +29 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +42 -0
- package/dist/semver.js.map +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
- package/src/engine.ts +141 -0
- package/src/envelope.ts +128 -0
- package/src/extensions/social.ts +179 -0
- package/src/extensions/trade.ts +210 -0
- package/src/extensions/zkp.ts +108 -0
- package/src/index.ts +30 -0
- package/src/json-schema.ts +141 -0
- package/src/lexicons/index.ts +68 -0
- package/src/lexicons/kash-id-audit.ts +61 -0
- package/src/lexicons/kash-id-preferences.ts +114 -0
- package/src/lexicons/kash-id-profile.ts +82 -0
- package/src/registry.ts +168 -0
- package/src/schema.ts +347 -0
- package/src/semver.ts +56 -0
- package/src/types.ts +27 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade Extension — domain lexicons (Duedale-shaped but Duedale-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* kash.trade.offer@1.0.0 — an item / service for sale.
|
|
5
|
+
* kash.trade.receipt@1.0.0 — a cryptographically signed completed transaction.
|
|
6
|
+
* kash.trade.status@1.0.0 — a lightweight logistics update.
|
|
7
|
+
*
|
|
8
|
+
* Import path: `@kashscript/lexicons/extensions/trade`.
|
|
9
|
+
*
|
|
10
|
+
* This file is an **extension**: the Foundry core never auto-loads it.
|
|
11
|
+
* Consumers explicitly register `TRADE_EXTENSION` against their
|
|
12
|
+
* `LexiconRegistry` to opt in to trade-domain validation.
|
|
13
|
+
*
|
|
14
|
+
* Prices follow ISO 4217 (currency codes) and store amounts as DECIMAL STRINGS
|
|
15
|
+
* to avoid IEEE-754 float drift. Locations use ISO 3166-2 region codes when
|
|
16
|
+
* available, falling back to free-form region strings.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { RECORD_REF_SCHEMA, type RecordRef } from "../envelope";
|
|
20
|
+
import type { LexiconDef } from "../registry";
|
|
21
|
+
import { S, type ObjectSchema } from "../schema";
|
|
22
|
+
|
|
23
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
24
|
+
* Money — ISO 4217 amount as a decimal string. AI-parseable.
|
|
25
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
26
|
+
|
|
27
|
+
const DECIMAL_AMOUNT_RE = /^(?:0|[1-9][0-9]{0,17})(?:\.[0-9]{1,8})?$/;
|
|
28
|
+
|
|
29
|
+
export type PriceUnit = "fixed" | "per-hour" | "per-day" | "per-month" | "per-unit";
|
|
30
|
+
|
|
31
|
+
export interface PriceSpec {
|
|
32
|
+
readonly amount: string;
|
|
33
|
+
readonly currency: string;
|
|
34
|
+
readonly unit?: PriceUnit;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const PRICE_SPEC: ObjectSchema = S.object(
|
|
38
|
+
{
|
|
39
|
+
amount: S.string({ pattern: DECIMAL_AMOUNT_RE, maxLength: 32 }),
|
|
40
|
+
currency: S.string({ format: "iso4217" }),
|
|
41
|
+
unit: S.optional(S.enum("fixed", "per-hour", "per-day", "per-month", "per-unit")),
|
|
42
|
+
},
|
|
43
|
+
["amount", "currency"],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
47
|
+
* kash.trade.offer@1.0.0
|
|
48
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
49
|
+
|
|
50
|
+
export const KASH_TRADE_OFFER_NSID = "kash.trade.offer" as const;
|
|
51
|
+
export const KASH_TRADE_OFFER_VERSION = { major: 1, minor: 0, patch: 0 } as const;
|
|
52
|
+
export const KASH_TRADE_OFFER_ID = "kash.trade.offer@1.0.0" as const;
|
|
53
|
+
|
|
54
|
+
export type TradeCategory = "goods" | "services" | "rentals" | "digital";
|
|
55
|
+
export type TradeStatus = "open" | "reserved" | "closed";
|
|
56
|
+
export type FulfilmentMode = "pickup" | "ship" | "digital";
|
|
57
|
+
|
|
58
|
+
export interface OfferMediaRef {
|
|
59
|
+
readonly cid: string;
|
|
60
|
+
readonly mimeType: "image/jpeg" | "image/png" | "image/webp";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface LocationSpec {
|
|
64
|
+
readonly region: string;
|
|
65
|
+
readonly handler: FulfilmentMode;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface KashTradeOfferBody {
|
|
69
|
+
readonly title: string;
|
|
70
|
+
readonly description?: string;
|
|
71
|
+
readonly category: TradeCategory;
|
|
72
|
+
readonly price: PriceSpec;
|
|
73
|
+
readonly media?: readonly OfferMediaRef[];
|
|
74
|
+
readonly location?: LocationSpec;
|
|
75
|
+
readonly validUntil?: string;
|
|
76
|
+
readonly status: TradeStatus;
|
|
77
|
+
readonly relatedPost?: RecordRef;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const OFFER_MEDIA_REF: ObjectSchema = S.object(
|
|
81
|
+
{
|
|
82
|
+
cid: S.string({ format: "cid" }),
|
|
83
|
+
mimeType: S.enum("image/jpeg", "image/png", "image/webp"),
|
|
84
|
+
},
|
|
85
|
+
["cid", "mimeType"],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const LOCATION_SPEC: ObjectSchema = S.object(
|
|
89
|
+
{
|
|
90
|
+
region: S.string({ minLength: 1, maxLength: 64 }),
|
|
91
|
+
handler: S.enum("pickup", "ship", "digital"),
|
|
92
|
+
},
|
|
93
|
+
["region", "handler"],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
export const KASH_TRADE_OFFER_BODY_SCHEMA: ObjectSchema = S.object(
|
|
97
|
+
{
|
|
98
|
+
title: S.string({ minLength: 1, maxLength: 140 }),
|
|
99
|
+
description: S.optional(S.string({ maxLength: 4000 })),
|
|
100
|
+
category: S.enum("goods", "services", "rentals", "digital"),
|
|
101
|
+
price: PRICE_SPEC,
|
|
102
|
+
media: S.optional(S.array(OFFER_MEDIA_REF, { maxItems: 8 })),
|
|
103
|
+
location: S.optional(LOCATION_SPEC),
|
|
104
|
+
validUntil: S.optional(S.string({ format: "iso8601" })),
|
|
105
|
+
status: S.enum("open", "reserved", "closed"),
|
|
106
|
+
relatedPost: S.optional(RECORD_REF_SCHEMA),
|
|
107
|
+
},
|
|
108
|
+
["title", "category", "price", "status"],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
112
|
+
* kash.trade.receipt@1.0.0
|
|
113
|
+
*
|
|
114
|
+
* Dual-receipt pattern: each party publishes their OWN receipt.
|
|
115
|
+
* Cross-domain interop: optional `linkedPost` references a `kash.social.post`.
|
|
116
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
117
|
+
|
|
118
|
+
export const KASH_TRADE_RECEIPT_NSID = "kash.trade.receipt" as const;
|
|
119
|
+
export const KASH_TRADE_RECEIPT_VERSION = { major: 1, minor: 0, patch: 0 } as const;
|
|
120
|
+
export const KASH_TRADE_RECEIPT_ID = "kash.trade.receipt@1.0.0" as const;
|
|
121
|
+
|
|
122
|
+
export type TradeRole = "buyer" | "seller";
|
|
123
|
+
|
|
124
|
+
export interface KashTradeReceiptBody {
|
|
125
|
+
readonly offer: RecordRef;
|
|
126
|
+
readonly counterparty: string;
|
|
127
|
+
readonly role: TradeRole;
|
|
128
|
+
readonly price: PriceSpec;
|
|
129
|
+
readonly settledAt: string;
|
|
130
|
+
readonly paymentRef?: string;
|
|
131
|
+
readonly counterpartyReceipt?: RecordRef;
|
|
132
|
+
readonly linkedPost?: RecordRef;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const KASH_TRADE_RECEIPT_BODY_SCHEMA: ObjectSchema = S.object(
|
|
136
|
+
{
|
|
137
|
+
offer: RECORD_REF_SCHEMA,
|
|
138
|
+
counterparty: S.string({ format: "did-ks" }),
|
|
139
|
+
role: S.enum("buyer", "seller"),
|
|
140
|
+
price: PRICE_SPEC,
|
|
141
|
+
settledAt: S.string({ format: "iso8601" }),
|
|
142
|
+
paymentRef: S.optional(S.string({ minLength: 1, maxLength: 128 })),
|
|
143
|
+
counterpartyReceipt: S.optional(RECORD_REF_SCHEMA),
|
|
144
|
+
linkedPost: S.optional(RECORD_REF_SCHEMA),
|
|
145
|
+
},
|
|
146
|
+
["offer", "counterparty", "role", "price", "settledAt"],
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
150
|
+
* kash.trade.status@1.0.0
|
|
151
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
152
|
+
|
|
153
|
+
export const KASH_TRADE_STATUS_NSID = "kash.trade.status" as const;
|
|
154
|
+
export const KASH_TRADE_STATUS_VERSION = { major: 1, minor: 0, patch: 0 } as const;
|
|
155
|
+
export const KASH_TRADE_STATUS_ID = "kash.trade.status@1.0.0" as const;
|
|
156
|
+
|
|
157
|
+
export type TradeState =
|
|
158
|
+
| "pending" | "shipped" | "in-transit" | "delivered"
|
|
159
|
+
| "received" | "disputed" | "refunded" | "cancelled";
|
|
160
|
+
|
|
161
|
+
export interface KashTradeStatusBody {
|
|
162
|
+
readonly receipt: RecordRef;
|
|
163
|
+
readonly state: TradeState;
|
|
164
|
+
readonly note?: string;
|
|
165
|
+
readonly trackingRef?: string;
|
|
166
|
+
readonly at?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export const KASH_TRADE_STATUS_BODY_SCHEMA: ObjectSchema = S.object(
|
|
170
|
+
{
|
|
171
|
+
receipt: RECORD_REF_SCHEMA,
|
|
172
|
+
state: S.enum("pending", "shipped", "in-transit", "delivered", "received", "disputed", "refunded", "cancelled"),
|
|
173
|
+
note: S.optional(S.string({ minLength: 1, maxLength: 280 })),
|
|
174
|
+
trackingRef: S.optional(S.string({ minLength: 1, maxLength: 128 })),
|
|
175
|
+
at: S.optional(S.string({ format: "iso8601" })),
|
|
176
|
+
},
|
|
177
|
+
["receipt", "state"],
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
181
|
+
* Extension bundle
|
|
182
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
183
|
+
|
|
184
|
+
/** All Trade-domain lexicons. Pass to `LexiconRegistry.registerAll()`. */
|
|
185
|
+
export const TRADE_EXTENSION: readonly LexiconDef[] = [
|
|
186
|
+
{
|
|
187
|
+
id: KASH_TRADE_OFFER_ID,
|
|
188
|
+
nsid: KASH_TRADE_OFFER_NSID,
|
|
189
|
+
semver: KASH_TRADE_OFFER_VERSION,
|
|
190
|
+
bodySchema: KASH_TRADE_OFFER_BODY_SCHEMA,
|
|
191
|
+
title: "Trade Offer",
|
|
192
|
+
description: "An item or service for sale — ISO 4217 price, optional location and media.",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: KASH_TRADE_RECEIPT_ID,
|
|
196
|
+
nsid: KASH_TRADE_RECEIPT_NSID,
|
|
197
|
+
semver: KASH_TRADE_RECEIPT_VERSION,
|
|
198
|
+
bodySchema: KASH_TRADE_RECEIPT_BODY_SCHEMA,
|
|
199
|
+
title: "Trade Receipt",
|
|
200
|
+
description: "Cryptographically signed completed transaction. Dual-receipt pattern; optionally links to a social post.",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: KASH_TRADE_STATUS_ID,
|
|
204
|
+
nsid: KASH_TRADE_STATUS_NSID,
|
|
205
|
+
semver: KASH_TRADE_STATUS_VERSION,
|
|
206
|
+
bodySchema: KASH_TRADE_STATUS_BODY_SCHEMA,
|
|
207
|
+
title: "Trade Status",
|
|
208
|
+
description: "Lightweight logistics update bound to a receipt.",
|
|
209
|
+
},
|
|
210
|
+
];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZKP Extension — privacy-bearing proof submissions.
|
|
3
|
+
*
|
|
4
|
+
* kash.zkp.proof-of-claim@1.0.0
|
|
5
|
+
* The on-the-wire shape a holder uses to submit a SNARK proof to a
|
|
6
|
+
* verifier. Pairs naturally with the {@link @kashscript/identity-zkp}
|
|
7
|
+
* prover surface but is transport-agnostic — verifiers can accept it
|
|
8
|
+
* over HTTP, Kafka, anywhere a Kash-Event flows.
|
|
9
|
+
*
|
|
10
|
+
* **F3 (audit) — one-shot-keypair pattern.** Every submission carries a
|
|
11
|
+
* `submitWith` discriminator that names the keypair model the envelope
|
|
12
|
+
* uses:
|
|
13
|
+
*
|
|
14
|
+
* • `"ephemeral-keypair"` — the holder mints a FRESH keypair just for
|
|
15
|
+
* this submission. The envelope's `kid` is the ephemeral DID; the
|
|
16
|
+
* verifier learns nothing about the holder's persistent identity.
|
|
17
|
+
* This is the unlinkable mode. Cross-verifier correlation requires
|
|
18
|
+
* the verifiers to combine OUT-OF-BAND signals (network, billing) —
|
|
19
|
+
* the wire format itself reveals nothing.
|
|
20
|
+
*
|
|
21
|
+
* • `"session-keypair"` — the holder uses a per-verifier session key
|
|
22
|
+
* (typically derived from a verifier-bound nonce). Submissions to
|
|
23
|
+
* the SAME verifier within the session are linkable; submissions
|
|
24
|
+
* across verifiers/sessions are not.
|
|
25
|
+
*
|
|
26
|
+
* • `"persistent-keypair"` — the holder uses their long-lived DID key.
|
|
27
|
+
* All submissions are correlatable. Use only when correlation is
|
|
28
|
+
* desired (e.g., audit-trail flows; high-trust authenticated grants).
|
|
29
|
+
*
|
|
30
|
+
* The lexicon DOES NOT enforce which discriminator is used — that is a
|
|
31
|
+
* holder choice — but verifiers MUST surface the chosen mode in their
|
|
32
|
+
* UX and decision logic.
|
|
33
|
+
*
|
|
34
|
+
* Import path: `@kashscript/lexicons/extensions/zkp`.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import type { LexiconDef } from "../registry";
|
|
38
|
+
import { S, type ObjectSchema } from "../schema";
|
|
39
|
+
|
|
40
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
41
|
+
* kash.zkp.proof-of-claim@1.0.0
|
|
42
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
43
|
+
|
|
44
|
+
export const KASH_ZKP_PROOF_OF_CLAIM_NSID = "kash.zkp.proof-of-claim" as const;
|
|
45
|
+
export const KASH_ZKP_PROOF_OF_CLAIM_VERSION = { major: 1, minor: 0, patch: 0 } as const;
|
|
46
|
+
export const KASH_ZKP_PROOF_OF_CLAIM_ID = "kash.zkp.proof-of-claim@1.0.0" as const;
|
|
47
|
+
|
|
48
|
+
/** Keypair-model discriminator. See module docstring. */
|
|
49
|
+
export type SubmitWith = "ephemeral-keypair" | "session-keypair" | "persistent-keypair";
|
|
50
|
+
|
|
51
|
+
/** Supported SNARK envelopes. Mirrors `@kashscript/identity-zkp` `ProofSystem`. */
|
|
52
|
+
export type ZkpProofSystem = "groth16" | "plonk" | "fflonk";
|
|
53
|
+
|
|
54
|
+
export interface KashZkpProofOfClaimBody {
|
|
55
|
+
/** The claim being proven, in plain language (display-only — never signed-into-circuit). */
|
|
56
|
+
readonly claim: string;
|
|
57
|
+
/** Stable circuit id (e.g., `"kash-range-proof-on-commitment-v1"`). */
|
|
58
|
+
readonly circuitId: string;
|
|
59
|
+
/** Proof system the envelope targets. */
|
|
60
|
+
readonly system: ZkpProofSystem;
|
|
61
|
+
/** The opaque proof envelope as emitted by snarkjs (any JSON shape). */
|
|
62
|
+
readonly proof: Readonly<Record<string, unknown>>;
|
|
63
|
+
/** Public signals — ordered, snarkjs decimal-string convention. */
|
|
64
|
+
readonly publicSignals: readonly string[];
|
|
65
|
+
/** Keypair model the holder used to submit. See module docstring. */
|
|
66
|
+
readonly submitWith: SubmitWith;
|
|
67
|
+
/**
|
|
68
|
+
* OPTIONAL audience identifier when the holder bound the proof to a specific
|
|
69
|
+
* verifier (typically the verifier's DID or origin URL). Verifiers SHOULD
|
|
70
|
+
* reject submissions whose `audience` does not match their own identity.
|
|
71
|
+
*/
|
|
72
|
+
readonly audience?: string;
|
|
73
|
+
/** OPTIONAL ISO-8601 expiry. Verifiers SHOULD reject after this instant. */
|
|
74
|
+
readonly expiresAt?: string;
|
|
75
|
+
/** OPTIONAL base58btc-encoded SHA-256 over `{ proof, publicSignals, audience, expiresAt }`. */
|
|
76
|
+
readonly bindingHashB58?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const KASH_ZKP_PROOF_OF_CLAIM_BODY_SCHEMA: ObjectSchema = S.object(
|
|
80
|
+
{
|
|
81
|
+
claim: S.string({ minLength: 1, maxLength: 280 }),
|
|
82
|
+
circuitId: S.string({ minLength: 1, maxLength: 128, pattern: /^[a-z0-9][a-z0-9-]*$/ }),
|
|
83
|
+
system: S.enum("groth16", "plonk", "fflonk"),
|
|
84
|
+
proof: S.object({}, []), // open shape — system-dependent
|
|
85
|
+
publicSignals: S.array(S.string({ maxLength: 96 }), { maxItems: 32 }),
|
|
86
|
+
submitWith: S.enum("ephemeral-keypair", "session-keypair", "persistent-keypair"),
|
|
87
|
+
audience: S.optional(S.string({ minLength: 1, maxLength: 512 })),
|
|
88
|
+
expiresAt: S.optional(S.string({ format: "iso8601" })),
|
|
89
|
+
bindingHashB58: S.optional(S.string({ minLength: 32, maxLength: 64 })),
|
|
90
|
+
},
|
|
91
|
+
["claim", "circuitId", "system", "proof", "publicSignals", "submitWith"],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/* ─────────────────────────────────────────────────────────────────
|
|
95
|
+
* Extension bundle
|
|
96
|
+
* ───────────────────────────────────────────────────────────────── */
|
|
97
|
+
|
|
98
|
+
/** All ZKP-domain lexicons. Pass to `LexiconRegistry.registerAll()`. */
|
|
99
|
+
export const ZKP_EXTENSION: readonly LexiconDef[] = [
|
|
100
|
+
{
|
|
101
|
+
id: KASH_ZKP_PROOF_OF_CLAIM_ID,
|
|
102
|
+
nsid: KASH_ZKP_PROOF_OF_CLAIM_NSID,
|
|
103
|
+
semver: KASH_ZKP_PROOF_OF_CLAIM_VERSION,
|
|
104
|
+
bodySchema: KASH_ZKP_PROOF_OF_CLAIM_BODY_SCHEMA,
|
|
105
|
+
title: "ZKP Proof of Claim",
|
|
106
|
+
description: "Wire envelope for submitting a SNARK proof to a verifier. Carries a submitWith discriminator that names the keypair-model the envelope uses, so verifiers can reason about linkability.",
|
|
107
|
+
},
|
|
108
|
+
];
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kashscript/lexicons — main export surface.
|
|
3
|
+
*
|
|
4
|
+
* Exposes:
|
|
5
|
+
* - Framework (types, schema DSL, semver, envelope) — the Box.
|
|
6
|
+
* - Registry + Engine (LexiconRegistry, validateEvent, getSchemaDefinition).
|
|
7
|
+
* - Standard Library (identity-tier lexicons) — the agnostic core Content.
|
|
8
|
+
*
|
|
9
|
+
* Domain Extensions (Social, Trade, …) are NOT auto-exported here.
|
|
10
|
+
* They live under subpath imports:
|
|
11
|
+
* - @kashscript/lexicons/extensions/social
|
|
12
|
+
* - @kashscript/lexicons/extensions/trade
|
|
13
|
+
*
|
|
14
|
+
* This package is zero-dependency. No network, no crypto, no storage —
|
|
15
|
+
* pure data modelling and validation.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export * from "./types";
|
|
19
|
+
export * from "./schema";
|
|
20
|
+
export * from "./semver";
|
|
21
|
+
export * from "./envelope";
|
|
22
|
+
export * from "./registry";
|
|
23
|
+
export * from "./engine";
|
|
24
|
+
export * from "./json-schema";
|
|
25
|
+
|
|
26
|
+
// Standard Library (identity tier) — lexicons consumers will almost always want.
|
|
27
|
+
// Domain content (social, trade, neuron) ships as separately-imported Extensions.
|
|
28
|
+
export * from "./lexicons";
|
|
29
|
+
|
|
30
|
+
export const LEXICONS_FRAMEWORK_VERSION = "1.0.0" as const;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a `Schema` (the internal DSL) to a JSON-Schema Draft 2020-12 object.
|
|
3
|
+
*
|
|
4
|
+
* Output is intended for consumption by external AI tools and documentation
|
|
5
|
+
* generators (Oreoasis ingestion, OpenAPI exporters, schema crawlers). The
|
|
6
|
+
* conversion is lossless for our DSL: every constraint we model maps to a
|
|
7
|
+
* documented JSON-Schema keyword.
|
|
8
|
+
*
|
|
9
|
+
* Kash-specific string formats (did-ks, cid, lexicon-id, …) are exposed via
|
|
10
|
+
* the `format` keyword AND as `pattern` regex so consumers without a custom
|
|
11
|
+
* format registry can still validate.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
FORMAT_PATTERNS,
|
|
16
|
+
type ArraySchema,
|
|
17
|
+
type EnumSchema,
|
|
18
|
+
type LiteralSchema,
|
|
19
|
+
type NumberSchema,
|
|
20
|
+
type ObjectSchema,
|
|
21
|
+
type RecordSchema,
|
|
22
|
+
type Schema,
|
|
23
|
+
type SchemaStringFormat,
|
|
24
|
+
type StringSchema,
|
|
25
|
+
type UnionSchema,
|
|
26
|
+
} from "./schema";
|
|
27
|
+
|
|
28
|
+
/** Minimal JSON-Schema object. Booleans (`true`/`false` schemas) are NOT emitted. */
|
|
29
|
+
export type JsonSchema = Readonly<Record<string, unknown>>;
|
|
30
|
+
|
|
31
|
+
/** Map Kash string-format identifiers to JSON-Schema `format` values.
|
|
32
|
+
* Standard formats (date-time, uri) use the JSON-Schema names; Kash-specific
|
|
33
|
+
* ones use a prefix. The regex source is reused from `FORMAT_PATTERNS` so we
|
|
34
|
+
* never duplicate the canonical regex. */
|
|
35
|
+
const FORMAT_NAMES: Record<SchemaStringFormat, string> = {
|
|
36
|
+
"did-ks": "did-ks",
|
|
37
|
+
"cid": "cid",
|
|
38
|
+
"iso8601": "date-time",
|
|
39
|
+
"url": "uri",
|
|
40
|
+
"multibase": "multibase",
|
|
41
|
+
"bcp47": "bcp47-language-tag",
|
|
42
|
+
"iso4217": "iso-4217-currency",
|
|
43
|
+
"lexicon-id": "kash-lexicon-id",
|
|
44
|
+
"nsid": "kash-nsid",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Convert any internal Schema to a JSON-Schema-Draft-2020-12 object. */
|
|
48
|
+
export function toJsonSchema(schema: Schema): JsonSchema {
|
|
49
|
+
switch (schema.kind) {
|
|
50
|
+
case "any":
|
|
51
|
+
return {};
|
|
52
|
+
case "boolean":
|
|
53
|
+
return { type: "boolean" };
|
|
54
|
+
case "literal":
|
|
55
|
+
return convertLiteral(schema);
|
|
56
|
+
case "enum":
|
|
57
|
+
return convertEnum(schema);
|
|
58
|
+
case "string":
|
|
59
|
+
return convertString(schema);
|
|
60
|
+
case "number":
|
|
61
|
+
return convertNumber(schema);
|
|
62
|
+
case "array":
|
|
63
|
+
return convertArray(schema);
|
|
64
|
+
case "object":
|
|
65
|
+
return convertObject(schema);
|
|
66
|
+
case "union":
|
|
67
|
+
return convertUnion(schema);
|
|
68
|
+
case "optional":
|
|
69
|
+
// Optionality is encoded by the parent object's `required` array.
|
|
70
|
+
return toJsonSchema(schema.inner);
|
|
71
|
+
case "record":
|
|
72
|
+
return convertRecord(schema);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function convertLiteral(s: LiteralSchema): JsonSchema {
|
|
77
|
+
return { const: s.value };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function convertEnum(s: EnumSchema): JsonSchema {
|
|
81
|
+
return { enum: [...s.values] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function convertString(s: StringSchema): JsonSchema {
|
|
85
|
+
const out: Record<string, unknown> = { type: "string" };
|
|
86
|
+
if (s.minLength !== undefined) out.minLength = s.minLength;
|
|
87
|
+
if (s.maxLength !== undefined) out.maxLength = s.maxLength;
|
|
88
|
+
if (s.pattern) out.pattern = s.pattern.source;
|
|
89
|
+
if (s.format) {
|
|
90
|
+
out.format = FORMAT_NAMES[s.format];
|
|
91
|
+
if (out.pattern === undefined) out.pattern = FORMAT_PATTERNS[s.format].source;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function convertNumber(s: NumberSchema): JsonSchema {
|
|
97
|
+
const out: Record<string, unknown> = { type: s.integer ? "integer" : "number" };
|
|
98
|
+
if (s.min !== undefined) out.minimum = s.min;
|
|
99
|
+
if (s.max !== undefined) out.maximum = s.max;
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function convertArray(s: ArraySchema): JsonSchema {
|
|
104
|
+
const out: Record<string, unknown> = {
|
|
105
|
+
type: "array",
|
|
106
|
+
items: toJsonSchema(s.items),
|
|
107
|
+
};
|
|
108
|
+
if (s.minItems !== undefined) out.minItems = s.minItems;
|
|
109
|
+
if (s.maxItems !== undefined) out.maxItems = s.maxItems;
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function convertObject(s: ObjectSchema): JsonSchema {
|
|
114
|
+
const properties: Record<string, JsonSchema> = {};
|
|
115
|
+
for (const k of Object.keys(s.properties)) {
|
|
116
|
+
const sub = s.properties[k]!;
|
|
117
|
+
properties[k] = toJsonSchema(sub);
|
|
118
|
+
}
|
|
119
|
+
const out: Record<string, unknown> = {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties,
|
|
122
|
+
additionalProperties: s.additionalProperties,
|
|
123
|
+
};
|
|
124
|
+
if (s.required.length > 0) out.required = [...s.required];
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function convertUnion(s: UnionSchema): JsonSchema {
|
|
129
|
+
return { anyOf: s.variants.map(toJsonSchema) };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function convertRecord(s: RecordSchema): JsonSchema {
|
|
133
|
+
const out: Record<string, unknown> = {
|
|
134
|
+
type: "object",
|
|
135
|
+
additionalProperties: toJsonSchema(s.values),
|
|
136
|
+
};
|
|
137
|
+
if (s.keys.pattern) {
|
|
138
|
+
out.propertyNames = { pattern: s.keys.pattern.source };
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Library — the agnostic, foundry-shipped lexicons.
|
|
3
|
+
*
|
|
4
|
+
* This registry slice contains ONLY the identity-tier lexicons:
|
|
5
|
+
* - kash.id.profile@1.0.0
|
|
6
|
+
* - kash.id.audit@1.0.0
|
|
7
|
+
* - kash.id.preferences@1.0.0
|
|
8
|
+
*
|
|
9
|
+
* Domain lexicons (Social, Trade, Neuron, third-party `app.*`) ship as
|
|
10
|
+
* Extensions and live under `src/extensions/<domain>.ts`. Consumers
|
|
11
|
+
* register what they need. The Core never auto-loads domain content —
|
|
12
|
+
* that is the load-bearing rule of the Box-vs-Content separation.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export * from "./kash-id-profile";
|
|
16
|
+
export * from "./kash-id-audit";
|
|
17
|
+
export * from "./kash-id-preferences";
|
|
18
|
+
|
|
19
|
+
import type { LexiconDef } from "../registry";
|
|
20
|
+
import {
|
|
21
|
+
KASH_PROFILE_BODY_SCHEMA, KASH_PROFILE_ID, KASH_PROFILE_NSID, KASH_PROFILE_VERSION,
|
|
22
|
+
} from "./kash-id-profile";
|
|
23
|
+
import {
|
|
24
|
+
KASH_AUDIT_BODY_SCHEMA, KASH_AUDIT_ID, KASH_AUDIT_NSID, KASH_AUDIT_VERSION,
|
|
25
|
+
} from "./kash-id-audit";
|
|
26
|
+
import {
|
|
27
|
+
KASH_PREFERENCES_BODY_SCHEMA, KASH_PREFERENCES_ID, KASH_PREFERENCES_NSID, KASH_PREFERENCES_VERSION,
|
|
28
|
+
} from "./kash-id-preferences";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The Standard Library — every identity-tier lexicon every Kash actor
|
|
32
|
+
* needs to interoperate at the protocol layer.
|
|
33
|
+
*
|
|
34
|
+
* Bootstrap pattern:
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { LexiconRegistry } from "@kashscript/lexicons";
|
|
38
|
+
* import { STANDARD_LIBRARY } from "@kashscript/lexicons";
|
|
39
|
+
*
|
|
40
|
+
* const registry = new LexiconRegistry().registerAll(STANDARD_LIBRARY);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export const STANDARD_LIBRARY: readonly LexiconDef[] = [
|
|
44
|
+
{
|
|
45
|
+
id: KASH_PROFILE_ID,
|
|
46
|
+
nsid: KASH_PROFILE_NSID,
|
|
47
|
+
semver: KASH_PROFILE_VERSION,
|
|
48
|
+
bodySchema: KASH_PROFILE_BODY_SCHEMA,
|
|
49
|
+
title: "Kash Profile",
|
|
50
|
+
description: "Sovereign user profile — display name, bio, avatar, pinned PDS URLs.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: KASH_AUDIT_ID,
|
|
54
|
+
nsid: KASH_AUDIT_NSID,
|
|
55
|
+
semver: KASH_AUDIT_VERSION,
|
|
56
|
+
bodySchema: KASH_AUDIT_BODY_SCHEMA,
|
|
57
|
+
title: "Kash Audit Entry",
|
|
58
|
+
description: "Append-only ledger of identity lifecycle events.",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: KASH_PREFERENCES_ID,
|
|
62
|
+
nsid: KASH_PREFERENCES_NSID,
|
|
63
|
+
semver: KASH_PREFERENCES_VERSION,
|
|
64
|
+
bodySchema: KASH_PREFERENCES_BODY_SCHEMA,
|
|
65
|
+
title: "Kash Preferences",
|
|
66
|
+
description: "App-agnostic settings — theme, language, AI consent flags.",
|
|
67
|
+
},
|
|
68
|
+
];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexicon: kash.id.audit@1.0.0
|
|
3
|
+
*
|
|
4
|
+
* Append-only ledger of identity-state changes. Each record references the
|
|
5
|
+
* corresponding op-log entry from the Identity Protocol so consumers can
|
|
6
|
+
* audit history without re-replaying the full chain.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { S, type ObjectSchema } from "../schema";
|
|
10
|
+
|
|
11
|
+
export const KASH_AUDIT_NSID = "kash.id.audit" as const;
|
|
12
|
+
export const KASH_AUDIT_VERSION = { major: 1, minor: 0, patch: 0 } as const;
|
|
13
|
+
export const KASH_AUDIT_ID = "kash.id.audit@1.0.0" as const;
|
|
14
|
+
|
|
15
|
+
/* ── types ──────────────────────────────────────────────────────── */
|
|
16
|
+
|
|
17
|
+
export type KashAuditEventType =
|
|
18
|
+
| "key-rotated"
|
|
19
|
+
| "key-revoked"
|
|
20
|
+
| "pds-migrated"
|
|
21
|
+
| "witnesses-changed"
|
|
22
|
+
| "quorum-set"
|
|
23
|
+
| "quorum-recovered"
|
|
24
|
+
| "deactivated";
|
|
25
|
+
|
|
26
|
+
export interface KashAuditBody {
|
|
27
|
+
/** Type of identity-state event being logged. */
|
|
28
|
+
readonly event: KashAuditEventType;
|
|
29
|
+
/** CID of the underlying op-log entry (Identity Protocol §3.4 head.opCid). */
|
|
30
|
+
readonly opCid: string;
|
|
31
|
+
/** Sequence number from the op log (Identity Protocol §4.2). */
|
|
32
|
+
readonly opSeq: number;
|
|
33
|
+
/** Op-log entry type ("update", "recover", "quorum-recover", etc.). */
|
|
34
|
+
readonly opType: string;
|
|
35
|
+
/** Human-readable summary for audit-log UIs. 1–280 chars. */
|
|
36
|
+
readonly summary: string;
|
|
37
|
+
/** Optional structured details; shape is per-event-type, free-form here. */
|
|
38
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ── runtime schema ─────────────────────────────────────────────── */
|
|
42
|
+
|
|
43
|
+
export const KASH_AUDIT_BODY_SCHEMA: ObjectSchema = S.object(
|
|
44
|
+
{
|
|
45
|
+
event: S.enum(
|
|
46
|
+
"key-rotated",
|
|
47
|
+
"key-revoked",
|
|
48
|
+
"pds-migrated",
|
|
49
|
+
"witnesses-changed",
|
|
50
|
+
"quorum-set",
|
|
51
|
+
"quorum-recovered",
|
|
52
|
+
"deactivated",
|
|
53
|
+
),
|
|
54
|
+
opCid: S.string({ format: "cid" }),
|
|
55
|
+
opSeq: S.number({ integer: true, min: 0 }),
|
|
56
|
+
opType: S.string({ minLength: 1, maxLength: 64 }),
|
|
57
|
+
summary: S.string({ minLength: 1, maxLength: 280 }),
|
|
58
|
+
details: S.optional(S.record(S.any())),
|
|
59
|
+
},
|
|
60
|
+
["event", "opCid", "opSeq", "opType", "summary"],
|
|
61
|
+
);
|