@naulon/shared 0.0.1
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/dist/attribution.d.ts +92 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/attribution.js +145 -0
- package/dist/attribution.js.map +1 -0
- package/dist/botAuthSign.d.ts +74 -0
- package/dist/botAuthSign.d.ts.map +1 -0
- package/dist/botAuthSign.js +90 -0
- package/dist/botAuthSign.js.map +1 -0
- package/dist/config.d.ts +131 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +400 -0
- package/dist/config.js.map +1 -0
- package/dist/credits.d.ts +7 -0
- package/dist/credits.d.ts.map +1 -0
- package/dist/credits.js +7 -0
- package/dist/credits.js.map +1 -0
- package/dist/eip3009.d.ts +69 -0
- package/dist/eip3009.d.ts.map +1 -0
- package/dist/eip3009.js +34 -0
- package/dist/eip3009.js.map +1 -0
- package/dist/eventsink.d.ts +19 -0
- package/dist/eventsink.d.ts.map +1 -0
- package/dist/eventsink.js +110 -0
- package/dist/eventsink.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/license.d.ts +178 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +252 -0
- package/dist/license.js.map +1 -0
- package/dist/networks.d.ts +63 -0
- package/dist/networks.d.ts.map +1 -0
- package/dist/networks.js +84 -0
- package/dist/networks.js.map +1 -0
- package/dist/observationsink.d.ts +22 -0
- package/dist/observationsink.d.ts.map +1 -0
- package/dist/observationsink.js +83 -0
- package/dist/observationsink.js.map +1 -0
- package/dist/publisher.d.ts +209 -0
- package/dist/publisher.d.ts.map +1 -0
- package/dist/publisher.js +2 -0
- package/dist/publisher.js.map +1 -0
- package/dist/settlementEmit.d.ts +31 -0
- package/dist/settlementEmit.d.ts.map +1 -0
- package/dist/settlementEmit.js +65 -0
- package/dist/settlementEmit.js.map +1 -0
- package/dist/supabase.d.ts +8 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +47 -0
- package/dist/supabase.js.map +1 -0
- package/dist/types.d.ts +154 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribution-as-settlement: turn an article's credits graph into a flat list
|
|
3
|
+
* of (wallet, share) payees. Co-authors can be composites that re-split among
|
|
4
|
+
* their own members, so resolution is recursive. Shares always sum to 1.
|
|
5
|
+
*
|
|
6
|
+
* This is the "recursive co-author splits read from the credits graph" that the
|
|
7
|
+
* innovation axis rewards — the payout rule is the attribution metadata itself.
|
|
8
|
+
*/
|
|
9
|
+
import type { ArticleCredits, AuthorShare, Contributor, WalletAddress } from "./types.ts";
|
|
10
|
+
/**
|
|
11
|
+
* Flatten an article's credits into payees. The same author appearing in
|
|
12
|
+
* multiple subtrees is merged into a single payee (shares added) so settlement
|
|
13
|
+
* is one transfer per wallet, not one per graph edge.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePayees(credits: ArticleCredits): AuthorShare[];
|
|
16
|
+
/**
|
|
17
|
+
* Tie-break for choosing the single on-chain recipient when two payees hold an
|
|
18
|
+
* equal top share. `"wallet"` (default) breaks ties lexicographically by address,
|
|
19
|
+
* so the recipient is a pure function of *who is credited* — independent of the
|
|
20
|
+
* order the credits graph happened to list them in (a reordered or swapped credits
|
|
21
|
+
* endpoint can't change who receives the on-chain leg). `"input"` keeps the
|
|
22
|
+
* credits-graph order (stable sort), matching the pre-config behavior.
|
|
23
|
+
*/
|
|
24
|
+
export type TieBreak = "wallet" | "input";
|
|
25
|
+
/**
|
|
26
|
+
* The single on-chain recipient for a toll: the highest-share payee. x402 settles
|
|
27
|
+
* to one `payTo`, and the settlement record flags this same wallet `primary`, so
|
|
28
|
+
* the on-chain leg and the recorded split MUST agree on it. This is the one source
|
|
29
|
+
* of that choice — imported by the x402 requirement builder and the settlement-body
|
|
30
|
+
* builder rather than computed twice. Throws on an empty payee list: a tolled event
|
|
31
|
+
* always has ≥1 payee, so empty is a malformed call, not a routing edge to paper over.
|
|
32
|
+
*/
|
|
33
|
+
export declare function primaryPayee(payees: AuthorShare[], tieBreak?: TieBreak): WalletAddress;
|
|
34
|
+
/** Integer micro-USDC allocation for one payee — the dust-free unit of truth. */
|
|
35
|
+
export interface MicroAllocation {
|
|
36
|
+
wallet: WalletAddress;
|
|
37
|
+
authorId: string;
|
|
38
|
+
micro: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Split an integer micro-USDC total across payees by share. Distributes whole
|
|
42
|
+
* micro units and hands any rounding remainder to the largest-share payee(s),
|
|
43
|
+
* so `Σ micro === totalMicro` exactly — no dust created or lost. This is the
|
|
44
|
+
* money-truth core: callers that need floats wrap it (splitAmount); callers
|
|
45
|
+
* that report integer micro-units to a publisher (settlementEmit) use it raw,
|
|
46
|
+
* never re-multiplying a float back up (which could break a strict sum check).
|
|
47
|
+
*/
|
|
48
|
+
export declare function splitMicro(totalMicro: number, payees: AuthorShare[]): MicroAllocation[];
|
|
49
|
+
/**
|
|
50
|
+
* Split a concrete toll amount across payees. Distributes whole micro-USDC
|
|
51
|
+
* units (6 decimals) and assigns any rounding remainder to the largest payee,
|
|
52
|
+
* so the sum of payouts exactly equals the toll — no dust created or lost.
|
|
53
|
+
*/
|
|
54
|
+
export declare function splitAmount(amountUsdc: number, payees: AuthorShare[]): {
|
|
55
|
+
wallet: WalletAddress;
|
|
56
|
+
authorId: string;
|
|
57
|
+
amountUsdc: number;
|
|
58
|
+
}[];
|
|
59
|
+
/**
|
|
60
|
+
* A custody-free split of the author price into on-chain settlement legs: ONE
|
|
61
|
+
* primary leg (settled synchronously at the gate — it gates the content) plus one
|
|
62
|
+
* deferred leg per OTHER co-author. Every leg is a DIRECT buyer→author transfer, so
|
|
63
|
+
* no wallet ever receives money owed to a different author — the lead is never a
|
|
64
|
+
* custodian of a co-author's cut (the custody-free / money-transmitter line).
|
|
65
|
+
*/
|
|
66
|
+
export interface AuthorLegSplit {
|
|
67
|
+
/** The highest-share payee — recipient of the synchronous, content-gating leg. */
|
|
68
|
+
primaryPayTo: WalletAddress;
|
|
69
|
+
/** The primary author's cut. Atomic micro-USDC, integer string. */
|
|
70
|
+
primaryAmountMicro: string;
|
|
71
|
+
/** One deferred leg per other co-author (a cut that floors to 0 micro is dropped). */
|
|
72
|
+
coauthorLegs: {
|
|
73
|
+
payTo: WalletAddress;
|
|
74
|
+
amountMicro: string;
|
|
75
|
+
}[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Split `atomicPrice` (integer micro-USDC) across `payees` into legs for
|
|
79
|
+
* split-at-source on-chain settlement. Reuses `splitMicro` so Σ(every leg) ===
|
|
80
|
+
* atomicPrice EXACTLY (no dust created or lost) AND the leg amounts match the
|
|
81
|
+
* earnings-ledger split byte-for-byte — so a co-author's on-chain *paid* equals
|
|
82
|
+
* their ledger *owed*, and reconciliation never shows phantom drift. The primary is
|
|
83
|
+
* `primaryPayee`, the same wallet the requirement builder and the settlement record
|
|
84
|
+
* already agree on. A single payee yields no co-author legs and a primary leg of the
|
|
85
|
+
* whole price (the stock single-author toll). A co-author cut that floors to 0 micro
|
|
86
|
+
* is dropped — a 0-amount transfer is meaningless and the facilitator would reject
|
|
87
|
+
* it; the sum still holds because `splitMicro` handed those units to the primary.
|
|
88
|
+
*/
|
|
89
|
+
export declare function splitAuthorLegs(payees: AuthorShare[], atomicPrice: number, tieBreak?: TieBreak): AuthorLegSplit;
|
|
90
|
+
/** Convenience for tests / fixtures: build a leaf contributor. */
|
|
91
|
+
export declare function author(authorId: string, wallet: string, weight?: number): Contributor;
|
|
92
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiC1F;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,EAAE,CAmBpE;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,GAAE,QAAmB,GAAG,aAAa,CAUhG;AAID,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,eAAe,EAAE,CAmBvF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,GACpB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAMnE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,YAAY,EAAE,aAAa,CAAC;IAC5B,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,YAAY,EAAE;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,EAAE,MAAM,EACnB,QAAQ,GAAE,QAAmB,GAC5B,cAAc,CAahB;AAED,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAErF"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { walletAddress } from "./types.js";
|
|
2
|
+
const EPSILON = 1e-9;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a credits graph into normalized author shares.
|
|
5
|
+
*
|
|
6
|
+
* @param parentShare the fraction of the whole toll flowing into this subtree
|
|
7
|
+
* (1 at the root).
|
|
8
|
+
*/
|
|
9
|
+
function resolveContributors(contributors, parentShare) {
|
|
10
|
+
const totalWeight = contributors.reduce((sum, c) => sum + (c.weight ?? 1), 0);
|
|
11
|
+
if (totalWeight <= 0) {
|
|
12
|
+
throw new Error("contributor weights sum to zero — cannot split");
|
|
13
|
+
}
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const c of contributors) {
|
|
16
|
+
const localShare = ((c.weight ?? 1) / totalWeight) * parentShare;
|
|
17
|
+
if (c.members && c.members.length > 0) {
|
|
18
|
+
// Composite contributor: recurse, passing down its slice of the toll.
|
|
19
|
+
out.push(...resolveContributors(c.members, localShare));
|
|
20
|
+
}
|
|
21
|
+
else if (c.wallet) {
|
|
22
|
+
out.push({ authorId: c.authorId, wallet: c.wallet, share: localShare });
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
throw new Error(`contributor ${c.authorId} has neither wallet nor members`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Flatten an article's credits into payees. The same author appearing in
|
|
32
|
+
* multiple subtrees is merged into a single payee (shares added) so settlement
|
|
33
|
+
* is one transfer per wallet, not one per graph edge.
|
|
34
|
+
*/
|
|
35
|
+
export function resolvePayees(credits) {
|
|
36
|
+
const raw = resolveContributors(credits.contributors, 1);
|
|
37
|
+
const byWallet = new Map();
|
|
38
|
+
for (const r of raw) {
|
|
39
|
+
const existing = byWallet.get(r.wallet);
|
|
40
|
+
if (existing) {
|
|
41
|
+
existing.share += r.share;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
byWallet.set(r.wallet, { ...r });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const merged = [...byWallet.values()];
|
|
48
|
+
const sum = merged.reduce((s, p) => s + p.share, 0);
|
|
49
|
+
if (Math.abs(sum - 1) > EPSILON) {
|
|
50
|
+
throw new Error(`shares sum to ${sum}, expected 1 — credits graph is malformed`);
|
|
51
|
+
}
|
|
52
|
+
return merged;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The single on-chain recipient for a toll: the highest-share payee. x402 settles
|
|
56
|
+
* to one `payTo`, and the settlement record flags this same wallet `primary`, so
|
|
57
|
+
* the on-chain leg and the recorded split MUST agree on it. This is the one source
|
|
58
|
+
* of that choice — imported by the x402 requirement builder and the settlement-body
|
|
59
|
+
* builder rather than computed twice. Throws on an empty payee list: a tolled event
|
|
60
|
+
* always has ≥1 payee, so empty is a malformed call, not a routing edge to paper over.
|
|
61
|
+
*/
|
|
62
|
+
export function primaryPayee(payees, tieBreak = "wallet") {
|
|
63
|
+
if (payees.length === 0) {
|
|
64
|
+
throw new Error("no payees — cannot resolve a primary on-chain recipient");
|
|
65
|
+
}
|
|
66
|
+
const sorted = [...payees].sort((a, b) => {
|
|
67
|
+
if (b.share !== a.share)
|
|
68
|
+
return b.share - a.share;
|
|
69
|
+
if (tieBreak === "wallet")
|
|
70
|
+
return a.wallet < b.wallet ? -1 : a.wallet > b.wallet ? 1 : 0;
|
|
71
|
+
return 0; // "input": stable sort preserves the credits-graph order
|
|
72
|
+
});
|
|
73
|
+
return sorted[0].wallet;
|
|
74
|
+
}
|
|
75
|
+
const MICRO = 1_000_000; // USDC has 6 decimals
|
|
76
|
+
/**
|
|
77
|
+
* Split an integer micro-USDC total across payees by share. Distributes whole
|
|
78
|
+
* micro units and hands any rounding remainder to the largest-share payee(s),
|
|
79
|
+
* so `Σ micro === totalMicro` exactly — no dust created or lost. This is the
|
|
80
|
+
* money-truth core: callers that need floats wrap it (splitAmount); callers
|
|
81
|
+
* that report integer micro-units to a publisher (settlementEmit) use it raw,
|
|
82
|
+
* never re-multiplying a float back up (which could break a strict sum check).
|
|
83
|
+
*/
|
|
84
|
+
export function splitMicro(totalMicro, payees) {
|
|
85
|
+
const allocations = payees.map((p) => ({
|
|
86
|
+
wallet: p.wallet,
|
|
87
|
+
authorId: p.authorId,
|
|
88
|
+
micro: Math.floor(totalMicro * p.share),
|
|
89
|
+
}));
|
|
90
|
+
const assigned = allocations.reduce((s, a) => s + a.micro, 0);
|
|
91
|
+
let remainder = totalMicro - assigned;
|
|
92
|
+
// Hand the rounding remainder to the largest-share payee(s), deterministically.
|
|
93
|
+
const order = [...allocations].sort((a, b) => b.micro - a.micro);
|
|
94
|
+
let i = 0;
|
|
95
|
+
while (remainder > 0 && order.length > 0) {
|
|
96
|
+
order[i % order.length].micro += 1;
|
|
97
|
+
remainder--;
|
|
98
|
+
i++;
|
|
99
|
+
}
|
|
100
|
+
return allocations;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Split a concrete toll amount across payees. Distributes whole micro-USDC
|
|
104
|
+
* units (6 decimals) and assigns any rounding remainder to the largest payee,
|
|
105
|
+
* so the sum of payouts exactly equals the toll — no dust created or lost.
|
|
106
|
+
*/
|
|
107
|
+
export function splitAmount(amountUsdc, payees) {
|
|
108
|
+
return splitMicro(Math.round(amountUsdc * MICRO), payees).map((a) => ({
|
|
109
|
+
wallet: a.wallet,
|
|
110
|
+
authorId: a.authorId,
|
|
111
|
+
amountUsdc: a.micro / MICRO,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Split `atomicPrice` (integer micro-USDC) across `payees` into legs for
|
|
116
|
+
* split-at-source on-chain settlement. Reuses `splitMicro` so Σ(every leg) ===
|
|
117
|
+
* atomicPrice EXACTLY (no dust created or lost) AND the leg amounts match the
|
|
118
|
+
* earnings-ledger split byte-for-byte — so a co-author's on-chain *paid* equals
|
|
119
|
+
* their ledger *owed*, and reconciliation never shows phantom drift. The primary is
|
|
120
|
+
* `primaryPayee`, the same wallet the requirement builder and the settlement record
|
|
121
|
+
* already agree on. A single payee yields no co-author legs and a primary leg of the
|
|
122
|
+
* whole price (the stock single-author toll). A co-author cut that floors to 0 micro
|
|
123
|
+
* is dropped — a 0-amount transfer is meaningless and the facilitator would reject
|
|
124
|
+
* it; the sum still holds because `splitMicro` handed those units to the primary.
|
|
125
|
+
*/
|
|
126
|
+
export function splitAuthorLegs(payees, atomicPrice, tieBreak = "wallet") {
|
|
127
|
+
const primary = primaryPayee(payees, tieBreak);
|
|
128
|
+
const allocations = splitMicro(atomicPrice, payees);
|
|
129
|
+
let primaryAmountMicro = "0";
|
|
130
|
+
const coauthorLegs = [];
|
|
131
|
+
for (const a of allocations) {
|
|
132
|
+
if (a.wallet === primary) {
|
|
133
|
+
primaryAmountMicro = String(a.micro);
|
|
134
|
+
}
|
|
135
|
+
else if (a.micro > 0) {
|
|
136
|
+
coauthorLegs.push({ payTo: a.wallet, amountMicro: String(a.micro) });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { primaryPayTo: primary, primaryAmountMicro, coauthorLegs };
|
|
140
|
+
}
|
|
141
|
+
/** Convenience for tests / fixtures: build a leaf contributor. */
|
|
142
|
+
export function author(authorId, wallet, weight) {
|
|
143
|
+
return { authorId, wallet: walletAddress(wallet), weight };
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=attribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.js","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Bot Auth — the SIGNING side of RFC 9421 HTTP Message Signatures, scoped
|
|
3
|
+
* to the web-bot-auth profile the tollgate's verifier (tollgate/botAuth.ts)
|
|
4
|
+
* enforces. This is the toll's own species signing its species marker: the
|
|
5
|
+
* wayfarer stamps outbound requests with a verifiable identity, and the gate
|
|
6
|
+
* serves (and self-signs) the key directory that identity points at.
|
|
7
|
+
*
|
|
8
|
+
* Lives in shared because both ends consume it — the wayfarer (sign requests)
|
|
9
|
+
* and the tollgate (serve the directory) — and neither may depend on the other.
|
|
10
|
+
* Self-contained on node:crypto, mirroring the verifier's zero-dep stance.
|
|
11
|
+
*/
|
|
12
|
+
import { type KeyObject } from "node:crypto";
|
|
13
|
+
/** Where every Web Bot Auth key directory lives (the drafts' well-known path). */
|
|
14
|
+
export declare const BOT_AUTH_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
15
|
+
export declare const BOT_AUTH_DIRECTORY_CONTENT_TYPE = "application/http-message-signatures-directory+json";
|
|
16
|
+
/** A usable Ed25519 signing identity: the private key plus the public half in
|
|
17
|
+
* the two forms the protocol needs (raw JWK x, RFC 7638 thumbprint keyid). */
|
|
18
|
+
export interface BotAuthKey {
|
|
19
|
+
privateKey: KeyObject;
|
|
20
|
+
/** Base64url raw public key — the JWK `x` member the directory publishes. */
|
|
21
|
+
x: string;
|
|
22
|
+
/** RFC 7638 JWK thumbprint — the `keyid` every signature carries. */
|
|
23
|
+
keyid: string;
|
|
24
|
+
}
|
|
25
|
+
/** RFC 7638 thumbprint of an Ed25519 public JWK: SHA-256 over the required
|
|
26
|
+
* members in lexicographic order (crv, kty, x). Byte-compatible with the
|
|
27
|
+
* verifier's jwkThumbprint — the round-trip test pins that. */
|
|
28
|
+
export declare function botAuthThumbprint(x: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Materialize the signing identity from a base64url 32-byte Ed25519 seed (the
|
|
31
|
+
* `BOT_AUTH_SIGNING_KEY` env). Throws on a malformed seed — the seed is
|
|
32
|
+
* operator config, and config fails loud (AGENTS.md), never silently unsigned.
|
|
33
|
+
*/
|
|
34
|
+
export declare function botAuthKeyFromSeed(seedB64url: string): BotAuthKey;
|
|
35
|
+
/** The three headers a signing agent attaches (lowercase names, ready to merge
|
|
36
|
+
* into a fetch). `signature-agent` is present only for the request profile —
|
|
37
|
+
* a directory response identifies itself by being AT the well-known URL. */
|
|
38
|
+
export interface BotAuthSignedHeaders {
|
|
39
|
+
"signature-input": string;
|
|
40
|
+
signature: string;
|
|
41
|
+
"signature-agent"?: string;
|
|
42
|
+
}
|
|
43
|
+
interface SignParams {
|
|
44
|
+
key: BotAuthKey;
|
|
45
|
+
/** The `@authority` the signature covers — the host:port the request targets. */
|
|
46
|
+
authority: string;
|
|
47
|
+
tag: "web-bot-auth" | "http-message-signatures-directory";
|
|
48
|
+
/** Signature-Agent value (request profile only): the directory host, e.g.
|
|
49
|
+
* "naulon.app", or an http://127.0.0.1:port fixture under the gate's
|
|
50
|
+
* BOT_AUTH_ALLOW_HTTP test flag. */
|
|
51
|
+
agent?: string;
|
|
52
|
+
createdSec?: number;
|
|
53
|
+
validitySec?: number;
|
|
54
|
+
label?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build + sign the RFC 9421 headers for one request (or directory response).
|
|
58
|
+
* Covered components: `("@authority")` — the CF operational profile's minimum,
|
|
59
|
+
* which is also what deployed signers (chatgpt.com) cover. The signature base
|
|
60
|
+
* reuses the exact Signature-Input member text, so signer and verifier agree
|
|
61
|
+
* byte-for-byte by construction.
|
|
62
|
+
*/
|
|
63
|
+
export declare function signBotAuth(params: SignParams): BotAuthSignedHeaders;
|
|
64
|
+
/** The directory body: a JWKS of our Ed25519 key(s). Minimal by design — the
|
|
65
|
+
* verifier reads kty/crv/x and ignores everything else. */
|
|
66
|
+
export declare function botAuthDirectoryBody(key: BotAuthKey): string;
|
|
67
|
+
/**
|
|
68
|
+
* Sign a directory RESPONSE (tag="http-message-signatures-directory") — the
|
|
69
|
+
* spec's binding of the published keys to the serving host. `authority` is the
|
|
70
|
+
* Host the client dialed; a verifier rebuilds the base from the URL it fetched.
|
|
71
|
+
*/
|
|
72
|
+
export declare function signBotAuthDirectory(key: BotAuthKey, authority: string, createdSec?: number): BotAuthSignedHeaders;
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=botAuthSign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"botAuthSign.d.ts","sourceRoot":"","sources":["../src/botAuthSign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAqE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAEhH,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,mDAAmD,CAAC;AACxF,eAAO,MAAM,+BAA+B,uDAAuD,CAAC;AAMpG;+EAC+E;AAC/E,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,SAAS,CAAC;IACtB,6EAA6E;IAC7E,CAAC,EAAE,MAAM,CAAC;IACV,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;gEAEgE;AAChE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAInD;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAejE;AAED;;6EAE6E;AAC7E,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,UAAU,UAAU;IAClB,GAAG,EAAE,UAAU,CAAC;IAChB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,cAAc,GAAG,mCAAmC,CAAC;IAC1D;;yCAEqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,oBAAoB,CAepE;AAED;4DAC4D;AAC5D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,oBAAoB,CAIlH"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Bot Auth — the SIGNING side of RFC 9421 HTTP Message Signatures, scoped
|
|
3
|
+
* to the web-bot-auth profile the tollgate's verifier (tollgate/botAuth.ts)
|
|
4
|
+
* enforces. This is the toll's own species signing its species marker: the
|
|
5
|
+
* wayfarer stamps outbound requests with a verifiable identity, and the gate
|
|
6
|
+
* serves (and self-signs) the key directory that identity points at.
|
|
7
|
+
*
|
|
8
|
+
* Lives in shared because both ends consume it — the wayfarer (sign requests)
|
|
9
|
+
* and the tollgate (serve the directory) — and neither may depend on the other.
|
|
10
|
+
* Self-contained on node:crypto, mirroring the verifier's zero-dep stance.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign } from "node:crypto";
|
|
13
|
+
/** Where every Web Bot Auth key directory lives (the drafts' well-known path). */
|
|
14
|
+
export const BOT_AUTH_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
15
|
+
export const BOT_AUTH_DIRECTORY_CONTENT_TYPE = "application/http-message-signatures-directory+json";
|
|
16
|
+
/** Default request-signature validity. CF's operational profile recommends ~1
|
|
17
|
+
* minute; the draft caps acceptance at 24h. Short = small replay window. */
|
|
18
|
+
const DEFAULT_VALIDITY_SEC = 60;
|
|
19
|
+
/** RFC 7638 thumbprint of an Ed25519 public JWK: SHA-256 over the required
|
|
20
|
+
* members in lexicographic order (crv, kty, x). Byte-compatible with the
|
|
21
|
+
* verifier's jwkThumbprint — the round-trip test pins that. */
|
|
22
|
+
export function botAuthThumbprint(x) {
|
|
23
|
+
return createHash("sha256")
|
|
24
|
+
.update(JSON.stringify({ crv: "Ed25519", kty: "OKP", x }))
|
|
25
|
+
.digest("base64url");
|
|
26
|
+
}
|
|
27
|
+
/** PKCS#8 DER prefix for a raw Ed25519 32-byte seed (RFC 8410 structure with
|
|
28
|
+
* fixed lengths — the seed is always exactly 32 bytes). */
|
|
29
|
+
const ED25519_PKCS8_PREFIX = Buffer.from("302e020100300506032b657004220420", "hex");
|
|
30
|
+
/**
|
|
31
|
+
* Materialize the signing identity from a base64url 32-byte Ed25519 seed (the
|
|
32
|
+
* `BOT_AUTH_SIGNING_KEY` env). Throws on a malformed seed — the seed is
|
|
33
|
+
* operator config, and config fails loud (AGENTS.md), never silently unsigned.
|
|
34
|
+
*/
|
|
35
|
+
export function botAuthKeyFromSeed(seedB64url) {
|
|
36
|
+
const seed = Buffer.from(seedB64url, "base64url");
|
|
37
|
+
if (seed.length !== 32) {
|
|
38
|
+
throw new Error(`BOT_AUTH_SIGNING_KEY must be a base64url 32-byte Ed25519 seed (got ${seed.length} bytes) — generate one with scripts/wba-keygen.mjs`);
|
|
39
|
+
}
|
|
40
|
+
const privateKey = createPrivateKey({
|
|
41
|
+
key: Buffer.concat([ED25519_PKCS8_PREFIX, seed]),
|
|
42
|
+
format: "der",
|
|
43
|
+
type: "pkcs8",
|
|
44
|
+
});
|
|
45
|
+
const jwk = createPublicKey(privateKey).export({ format: "jwk" });
|
|
46
|
+
if (typeof jwk.x !== "string")
|
|
47
|
+
throw new Error("failed to derive the Ed25519 public key from the seed");
|
|
48
|
+
return { privateKey, x: jwk.x, keyid: botAuthThumbprint(jwk.x) };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build + sign the RFC 9421 headers for one request (or directory response).
|
|
52
|
+
* Covered components: `("@authority")` — the CF operational profile's minimum,
|
|
53
|
+
* which is also what deployed signers (chatgpt.com) cover. The signature base
|
|
54
|
+
* reuses the exact Signature-Input member text, so signer and verifier agree
|
|
55
|
+
* byte-for-byte by construction.
|
|
56
|
+
*/
|
|
57
|
+
export function signBotAuth(params) {
|
|
58
|
+
const created = params.createdSec ?? Math.floor(Date.now() / 1000);
|
|
59
|
+
const expires = created + (params.validitySec ?? DEFAULT_VALIDITY_SEC);
|
|
60
|
+
const label = params.label ?? "sig1";
|
|
61
|
+
const member = `("@authority");created=${created};expires=${expires};keyid="${params.key.keyid}";tag="${params.tag}"`;
|
|
62
|
+
const base = `"@authority": ${params.authority.toLowerCase()}\n"@signature-params": ${member}`;
|
|
63
|
+
const sig = cryptoSign(null, Buffer.from(base, "utf8"), params.key.privateKey).toString("base64");
|
|
64
|
+
const headers = {
|
|
65
|
+
"signature-input": `${label}=${member}`,
|
|
66
|
+
signature: `${label}=:${sig}:`,
|
|
67
|
+
};
|
|
68
|
+
// CF profile: Signature-Agent MUST be a plain quoted string (rev-00's
|
|
69
|
+
// dictionary form is rejected by deployed verifiers).
|
|
70
|
+
if (params.agent !== undefined)
|
|
71
|
+
headers["signature-agent"] = `"${params.agent}"`;
|
|
72
|
+
return headers;
|
|
73
|
+
}
|
|
74
|
+
/** The directory body: a JWKS of our Ed25519 key(s). Minimal by design — the
|
|
75
|
+
* verifier reads kty/crv/x and ignores everything else. */
|
|
76
|
+
export function botAuthDirectoryBody(key) {
|
|
77
|
+
return JSON.stringify({ keys: [{ kty: "OKP", crv: "Ed25519", x: key.x }] });
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Sign a directory RESPONSE (tag="http-message-signatures-directory") — the
|
|
81
|
+
* spec's binding of the published keys to the serving host. `authority` is the
|
|
82
|
+
* Host the client dialed; a verifier rebuilds the base from the URL it fetched.
|
|
83
|
+
*/
|
|
84
|
+
export function signBotAuthDirectory(key, authority, createdSec) {
|
|
85
|
+
const params = { key, authority, tag: "http-message-signatures-directory", validitySec: 300 };
|
|
86
|
+
if (createdSec !== undefined)
|
|
87
|
+
params.createdSec = createdSec;
|
|
88
|
+
return signBotAuth(params);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=botAuthSign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"botAuthSign.js","sourceRoot":"","sources":["../src/botAuthSign.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const configSchema: z.ZodObject<{
|
|
3
|
+
PAYMENT_MODE: z.ZodDefault<z.ZodEnum<{
|
|
4
|
+
mock: "mock";
|
|
5
|
+
gateway: "gateway";
|
|
6
|
+
}>>;
|
|
7
|
+
SETTLEMENT_NETWORK: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
arcTestnet: "arcTestnet";
|
|
9
|
+
baseSepolia: "baseSepolia";
|
|
10
|
+
base: "base";
|
|
11
|
+
}>>;
|
|
12
|
+
CIRCLE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
13
|
+
GATEWAY_API_URL: z.ZodOptional<z.ZodString>;
|
|
14
|
+
RELAYER_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
15
|
+
USDC_EIP712_NAME: z.ZodOptional<z.ZodString>;
|
|
16
|
+
X402_MAX_TIMEOUT_SECONDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
17
|
+
TOLLGATE_PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
18
|
+
ORIGIN_URL: z.ZodDefault<z.ZodString>;
|
|
19
|
+
DEFAULT_PRICE_USDC: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
20
|
+
CITATION_MULTIPLIER: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
21
|
+
ARTICLE_PATH_PREFIXES: z.ZodDefault<z.ZodString>;
|
|
22
|
+
TOLLGATE_SECRET: z.ZodOptional<z.ZodString>;
|
|
23
|
+
NONCE_TTL_SECONDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
24
|
+
RATE_LIMIT_RPM: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
25
|
+
RATE_LIMIT_BURST: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
26
|
+
TRUST_PROXY: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
27
|
+
true: "true";
|
|
28
|
+
false: "false";
|
|
29
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
30
|
+
BOT_AUTH_ALLOW_HTTP: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
31
|
+
true: "true";
|
|
32
|
+
false: "false";
|
|
33
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
34
|
+
BOT_AUTH_SIGNING_KEY: z.ZodOptional<z.ZodString>;
|
|
35
|
+
BOT_AUTH_SIGNATURE_AGENT: z.ZodOptional<z.ZodString>;
|
|
36
|
+
CREDITS_API_URL: z.ZodOptional<z.ZodString>;
|
|
37
|
+
CREDITS_API_TOKEN: z.ZodOptional<z.ZodString>;
|
|
38
|
+
CREDITS_FIXTURES: z.ZodDefault<z.ZodString>;
|
|
39
|
+
CREDITS_SETTLEMENT_SECRET: z.ZodOptional<z.ZodString>;
|
|
40
|
+
SETTLEMENT_OUTBOX_PATH: z.ZodDefault<z.ZodString>;
|
|
41
|
+
SETTLEMENT_MAX_ATTEMPTS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
42
|
+
SETTLEMENT_TIMEOUT_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
43
|
+
SETTLEMENT_DRAIN_INTERVAL_MS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
44
|
+
BUYER_ADDRESS: z.ZodOptional<z.ZodString>;
|
|
45
|
+
BUYER_PRIVATE_KEY: z.ZodOptional<z.ZodString>;
|
|
46
|
+
WAYFARER_BUDGET_USDC: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
47
|
+
WAYFARER_TOLL_TOLERANCE_BPS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
48
|
+
WAYFARER_MIN_VALIDITY_SECONDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
49
|
+
WAYFARER_ALLOW_DOMAINS: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string[] | undefined, string | undefined>>;
|
|
50
|
+
WAYFARER_DENY_DOMAINS: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string[] | undefined, string | undefined>>;
|
|
51
|
+
WAYFARER_PER_DOMAIN_CAP: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
52
|
+
WAYFARER_APPROVAL_USDC: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
53
|
+
WAYFARER_KILL_SWITCH: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<boolean, string>>;
|
|
54
|
+
OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
|
|
55
|
+
DEPOSIT_AMOUNT_USDC: z.ZodDefault<z.ZodString>;
|
|
56
|
+
TOLLGATE_URL: z.ZodOptional<z.ZodString>;
|
|
57
|
+
CATALOG_URL: z.ZodOptional<z.ZodString>;
|
|
58
|
+
PUBLISHER_URL: z.ZodOptional<z.ZodString>;
|
|
59
|
+
RSS_URL: z.ZodOptional<z.ZodString>;
|
|
60
|
+
SITEMAP_URL: z.ZodOptional<z.ZodString>;
|
|
61
|
+
WAYFARER_LICENSE_PATH: z.ZodDefault<z.ZodString>;
|
|
62
|
+
DASHBOARD_PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
63
|
+
DASHBOARD_BIND: z.ZodDefault<z.ZodString>;
|
|
64
|
+
EVENTS_BACKEND: z.ZodDefault<z.ZodEnum<{
|
|
65
|
+
jsonl: "jsonl";
|
|
66
|
+
supabase: "supabase";
|
|
67
|
+
}>>;
|
|
68
|
+
NONCE_BACKEND: z.ZodDefault<z.ZodEnum<{
|
|
69
|
+
supabase: "supabase";
|
|
70
|
+
memory: "memory";
|
|
71
|
+
}>>;
|
|
72
|
+
PENDING_LEGS_BACKEND: z.ZodDefault<z.ZodEnum<{
|
|
73
|
+
supabase: "supabase";
|
|
74
|
+
memory: "memory";
|
|
75
|
+
}>>;
|
|
76
|
+
SUPABASE_URL: z.ZodOptional<z.ZodString>;
|
|
77
|
+
SUPABASE_SERVICE_KEY: z.ZodOptional<z.ZodString>;
|
|
78
|
+
SUPABASE_EVENTS_TABLE: z.ZodDefault<z.ZodString>;
|
|
79
|
+
SUPABASE_NONCES_TABLE: z.ZodDefault<z.ZodString>;
|
|
80
|
+
SUPABASE_PENDING_LEGS_TABLE: z.ZodDefault<z.ZodString>;
|
|
81
|
+
SUPABASE_REVOCATIONS_TABLE: z.ZodDefault<z.ZodString>;
|
|
82
|
+
OBSERVATIONS_BACKEND: z.ZodDefault<z.ZodEnum<{
|
|
83
|
+
jsonl: "jsonl";
|
|
84
|
+
supabase: "supabase";
|
|
85
|
+
off: "off";
|
|
86
|
+
}>>;
|
|
87
|
+
SUPABASE_OBSERVATIONS_TABLE: z.ZodDefault<z.ZodString>;
|
|
88
|
+
LICENSES_ENABLED: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
89
|
+
true: "true";
|
|
90
|
+
false: "false";
|
|
91
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
92
|
+
LICENSE_SIGNING_KEY: z.ZodOptional<z.ZodString>;
|
|
93
|
+
LICENSE_TTL_SECONDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
94
|
+
LICENSE_ISSUER: z.ZodOptional<z.ZodString>;
|
|
95
|
+
LICENSE_PAYEES_MODE: z.ZodDefault<z.ZodEnum<{
|
|
96
|
+
full: "full";
|
|
97
|
+
hashed: "hashed";
|
|
98
|
+
}>>;
|
|
99
|
+
LICENSE_ONLINE_CHECK: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
100
|
+
true: "true";
|
|
101
|
+
false: "false";
|
|
102
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
103
|
+
LICENSE_POP: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
104
|
+
true: "true";
|
|
105
|
+
false: "false";
|
|
106
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
107
|
+
LICENSE_POP_WINDOW_SECONDS: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
108
|
+
EVENTS_PATH: z.ZodDefault<z.ZodString>;
|
|
109
|
+
OBSERVATIONS_PATH: z.ZodDefault<z.ZodString>;
|
|
110
|
+
PAYOUTS_PATH: z.ZodDefault<z.ZodString>;
|
|
111
|
+
MIN_PAYOUT_USDC: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
112
|
+
PRIMARY_PAYEE_TIEBREAK: z.ZodDefault<z.ZodEnum<{
|
|
113
|
+
wallet: "wallet";
|
|
114
|
+
input: "input";
|
|
115
|
+
}>>;
|
|
116
|
+
COAUTHOR_ONCHAIN_SPLIT: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
|
|
117
|
+
true: "true";
|
|
118
|
+
false: "false";
|
|
119
|
+
}>>, z.ZodTransform<boolean, "true" | "false">>;
|
|
120
|
+
}, z.core.$strip>;
|
|
121
|
+
export type Config = z.infer<typeof configSchema>;
|
|
122
|
+
export declare function getConfig(): Config;
|
|
123
|
+
/**
|
|
124
|
+
* Drop the memoized config so the next `getConfig()` re-reads `process.env`.
|
|
125
|
+
* A test seam for the env-dependent branches the singleton otherwise freezes
|
|
126
|
+
* (e.g. the Circle-key settlement guard) — not for production hot-reload.
|
|
127
|
+
*/
|
|
128
|
+
export declare function resetConfig(): void;
|
|
129
|
+
/** Assert a set of keys are present (call at a component's boot once it knows what it needs). */
|
|
130
|
+
export declare function requireKeys(cfg: Config, keys: (keyof Config)[]): void;
|
|
131
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2CxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqVvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD,wBAAgB,SAAS,IAAI,MAAM,CAWlC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,MAAM,CAAC,EAAE,GAAG,IAAI,CAKrE"}
|