@noodleseed/one 0.37.0 → 0.38.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/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/billing-ops.d.ts +3 -0
- package/dist/commands/billing-ops.d.ts.map +1 -0
- package/dist/commands/billing-ops.js +144 -0
- package/dist/commands/billing-ops.js.map +1 -0
- package/dist/commands/catalog-data-resources.d.ts.map +1 -1
- package/dist/commands/catalog-data-resources.js +13 -0
- package/dist/commands/catalog-data-resources.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.d.ts.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
- package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.d.ts.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +1 -0
- package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.d.ts.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.js +5 -0
- package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.d.ts +6 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.js +23 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory-serialization.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory.d.ts +32 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory.js +185 -0
- package/node_modules/@noodle-borg/service/dist/billing/in-memory.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/migration.d.ts +84 -0
- package/node_modules/@noodle-borg/service/dist/billing/migration.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/migration.js +301 -0
- package/node_modules/@noodle-borg/service/dist/billing/migration.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/model.d.ts +85 -0
- package/node_modules/@noodle-borg/service/dist/billing/model.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/model.js +134 -0
- package/node_modules/@noodle-borg/service/dist/billing/model.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-records.d.ts +66 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-records.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-records.js +163 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-records.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.d.ts +4 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.js +148 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres-schema.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres.d.ts +26 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres.js +273 -0
- package/node_modules/@noodle-borg/service/dist/billing/postgres.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/store.d.ts +27 -0
- package/node_modules/@noodle-borg/service/dist/billing/store.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/billing/store.js +2 -0
- package/node_modules/@noodle-borg/service/dist/billing/store.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/index.d.ts +3 -0
- package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/index.js +2 -0
- package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/options.d.ts +4 -1
- package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/billing.d.ts +14 -0
- package/node_modules/@noodle-borg/service/dist/routes/billing.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/routes/billing.js +76 -0
- package/node_modules/@noodle-borg/service/dist/routes/billing.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.d.ts +25 -0
- package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.js +58 -0
- package/node_modules/@noodle-borg/service/dist/routes/commercial-dispatch.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/routes/control-plane.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +17 -0
- package/node_modules/@noodle-borg/service/dist/routes/control-plane.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/serve.js +8 -0
- package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/service.js +16 -26
- package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts +2 -1
- package/node_modules/@noodle-borg/service/dist/store/control-plane.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/control-plane.js +81 -48
- package/node_modules/@noodle-borg/service/dist/store/control-plane.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js +18 -6
- package/node_modules/@noodle-borg/service/dist/store/postgres-control-plane.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-schema.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js +2 -0
- package/node_modules/@noodle-borg/service/dist/store/postgres-schema.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-signup.d.ts +14 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-signup.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js +28 -0
- package/node_modules/@noodle-borg/service/dist/store/postgres-signup.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/postgres.js +7 -3
- package/node_modules/@noodle-borg/service/dist/store/postgres.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/validate.d.ts +4 -0
- package/node_modules/@noodle-borg/service/dist/store/validate.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store/validate.js +13 -0
- package/node_modules/@noodle-borg/service/dist/store/validate.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store.d.ts +5 -2
- package/node_modules/@noodle-borg/service/dist/store.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/store.js +1 -1
- package/node_modules/@noodle-borg/service/dist/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Authority that assigned every current hosted control-plane subject, including Noodle OAuth tokens. */
|
|
2
|
+
export declare const GOOGLE_CONTROL_PLANE_ISSUER = "https://accounts.google.com";
|
|
3
|
+
export type BillingAccountState = 'active' | 'draft' | 'inactive';
|
|
4
|
+
export type BillingAccountMemberRole = 'owner' | 'admin' | 'viewer';
|
|
5
|
+
export interface BillingIdentityRef {
|
|
6
|
+
readonly identityIssuer: string;
|
|
7
|
+
readonly subject: string;
|
|
8
|
+
}
|
|
9
|
+
export interface BillingIdentityInput extends BillingIdentityRef {
|
|
10
|
+
readonly email?: string;
|
|
11
|
+
}
|
|
12
|
+
export type BillingEntitlementValue = boolean | number | string | null;
|
|
13
|
+
export type BillingEntitlements = Readonly<Record<string, BillingEntitlementValue>>;
|
|
14
|
+
export interface BillingAccountRecord {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly displayName: string;
|
|
17
|
+
readonly state: BillingAccountState;
|
|
18
|
+
readonly createdAt: string;
|
|
19
|
+
}
|
|
20
|
+
export interface BillingAccountMemberRecord extends BillingIdentityRef {
|
|
21
|
+
readonly billingAccountId: string;
|
|
22
|
+
readonly email: string;
|
|
23
|
+
readonly role: BillingAccountMemberRole;
|
|
24
|
+
readonly createdAt: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PlanVersionRecord {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly planCode: string;
|
|
29
|
+
readonly version: number;
|
|
30
|
+
readonly effectiveFrom: string;
|
|
31
|
+
readonly entitlements: BillingEntitlements;
|
|
32
|
+
readonly contentChecksum: string;
|
|
33
|
+
readonly createdAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface EntitlementSnapshotRecord {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly billingAccountId: string;
|
|
38
|
+
readonly version: number;
|
|
39
|
+
readonly planVersionId: string;
|
|
40
|
+
readonly planCode: string;
|
|
41
|
+
readonly planVersion: number;
|
|
42
|
+
readonly sourceKind: 'system-free';
|
|
43
|
+
readonly generationReason: 'default-account-created';
|
|
44
|
+
readonly verificationStatus: 'verified';
|
|
45
|
+
readonly effectiveFrom: string;
|
|
46
|
+
readonly monthlyUsageAnchor: string;
|
|
47
|
+
readonly entitlements: BillingEntitlements;
|
|
48
|
+
readonly contentChecksum: string;
|
|
49
|
+
readonly verifiedAt: string;
|
|
50
|
+
readonly createdAt: string;
|
|
51
|
+
}
|
|
52
|
+
export interface OrgBillingLinkRecord {
|
|
53
|
+
readonly orgSlug: string;
|
|
54
|
+
readonly billingAccountId: string;
|
|
55
|
+
readonly homeFreeBillingAccountId: string;
|
|
56
|
+
readonly homeOwner: BillingIdentityRef;
|
|
57
|
+
readonly fallbackBillingAccountId: string;
|
|
58
|
+
readonly version: number;
|
|
59
|
+
readonly active: true;
|
|
60
|
+
readonly linkedAt: string;
|
|
61
|
+
readonly linkedBy: BillingIdentityRef;
|
|
62
|
+
readonly reason: string;
|
|
63
|
+
}
|
|
64
|
+
export declare const FREE_ENTITLEMENTS: BillingEntitlements;
|
|
65
|
+
export declare const FREE_PLAN_VERSION: PlanVersionRecord;
|
|
66
|
+
export declare function createDefaultFreeBillingRecords(input: {
|
|
67
|
+
readonly identity: BillingIdentityRef;
|
|
68
|
+
readonly email: string;
|
|
69
|
+
readonly displayName?: string;
|
|
70
|
+
readonly createdAt: string;
|
|
71
|
+
}): {
|
|
72
|
+
readonly account: BillingAccountRecord;
|
|
73
|
+
readonly member: BillingAccountMemberRecord;
|
|
74
|
+
readonly snapshot: EntitlementSnapshotRecord;
|
|
75
|
+
};
|
|
76
|
+
export declare function billingIdentityKey(identity: BillingIdentityRef): string;
|
|
77
|
+
export declare function billingMemberKey(input: BillingIdentityRef & {
|
|
78
|
+
readonly billingAccountId: string;
|
|
79
|
+
}): string;
|
|
80
|
+
export declare function normalizeBillingIdentity(identity: BillingIdentityRef): BillingIdentityRef;
|
|
81
|
+
export declare function normalizeBillingEmail(email: string): string;
|
|
82
|
+
export declare function validateBillingAccountId(value: string): string;
|
|
83
|
+
export declare function validateBillingLinkReason(value: string): string;
|
|
84
|
+
export declare function billingContentChecksum(value: unknown): string;
|
|
85
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/billing/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { canonicalJsonFingerprint } from '../policy-idempotency.js';
|
|
3
|
+
/** Authority that assigned every current hosted control-plane subject, including Noodle OAuth tokens. */
|
|
4
|
+
export const GOOGLE_CONTROL_PLANE_ISSUER = 'https://accounts.google.com';
|
|
5
|
+
export const FREE_ENTITLEMENTS = Object.freeze({
|
|
6
|
+
'usage.mcp_calls.included': 1_000_000,
|
|
7
|
+
'usage.mcp_calls.overage': false,
|
|
8
|
+
'apps.production.max': 1,
|
|
9
|
+
'access.owner_or_public': true,
|
|
10
|
+
'access.org_members': false,
|
|
11
|
+
'config.managed_secrets': false,
|
|
12
|
+
'deploy.history_rollback': 'basic',
|
|
13
|
+
'assets.packaged': 'basic',
|
|
14
|
+
'domains.custom': false,
|
|
15
|
+
'governance.policy': false,
|
|
16
|
+
'governance.audit': false,
|
|
17
|
+
'support.email': 'community',
|
|
18
|
+
'support.priority': false,
|
|
19
|
+
'deployment.dedicated': false,
|
|
20
|
+
'deployment.byo_cloud': false,
|
|
21
|
+
'identity.sso_scim': false,
|
|
22
|
+
'connectivity.private': false,
|
|
23
|
+
sla: false,
|
|
24
|
+
});
|
|
25
|
+
const FREE_PLAN_CONTENT = {
|
|
26
|
+
id: 'free@1',
|
|
27
|
+
planCode: 'free',
|
|
28
|
+
version: 1,
|
|
29
|
+
effectiveFrom: '2026-07-11T00:00:00.000Z',
|
|
30
|
+
entitlements: FREE_ENTITLEMENTS,
|
|
31
|
+
};
|
|
32
|
+
export const FREE_PLAN_VERSION = Object.freeze({
|
|
33
|
+
...FREE_PLAN_CONTENT,
|
|
34
|
+
contentChecksum: billingContentChecksum(FREE_PLAN_CONTENT),
|
|
35
|
+
createdAt: FREE_PLAN_CONTENT.effectiveFrom,
|
|
36
|
+
});
|
|
37
|
+
export function createDefaultFreeBillingRecords(input) {
|
|
38
|
+
const billingAccountId = `ba_${randomUUID()}`;
|
|
39
|
+
const snapshotId = `es_${randomUUID()}`;
|
|
40
|
+
const email = normalizeBillingEmail(input.email);
|
|
41
|
+
const account = {
|
|
42
|
+
id: billingAccountId,
|
|
43
|
+
displayName: input.displayName?.trim() || email,
|
|
44
|
+
state: 'active',
|
|
45
|
+
createdAt: input.createdAt,
|
|
46
|
+
};
|
|
47
|
+
const member = {
|
|
48
|
+
billingAccountId,
|
|
49
|
+
...input.identity,
|
|
50
|
+
email,
|
|
51
|
+
role: 'owner',
|
|
52
|
+
createdAt: input.createdAt,
|
|
53
|
+
};
|
|
54
|
+
const snapshotContent = {
|
|
55
|
+
billingAccountId,
|
|
56
|
+
version: 1,
|
|
57
|
+
planVersionId: FREE_PLAN_VERSION.id,
|
|
58
|
+
effectiveFrom: input.createdAt,
|
|
59
|
+
monthlyUsageAnchor: input.createdAt,
|
|
60
|
+
entitlements: FREE_ENTITLEMENTS,
|
|
61
|
+
};
|
|
62
|
+
const snapshot = {
|
|
63
|
+
id: snapshotId,
|
|
64
|
+
...snapshotContent,
|
|
65
|
+
planCode: FREE_PLAN_VERSION.planCode,
|
|
66
|
+
planVersion: FREE_PLAN_VERSION.version,
|
|
67
|
+
sourceKind: 'system-free',
|
|
68
|
+
generationReason: 'default-account-created',
|
|
69
|
+
verificationStatus: 'verified',
|
|
70
|
+
contentChecksum: billingContentChecksum(snapshotContent),
|
|
71
|
+
verifiedAt: input.createdAt,
|
|
72
|
+
createdAt: input.createdAt,
|
|
73
|
+
};
|
|
74
|
+
return { account, member, snapshot };
|
|
75
|
+
}
|
|
76
|
+
export function billingIdentityKey(identity) {
|
|
77
|
+
const issuer = normalizeIdentityPart('identityIssuer', identity.identityIssuer);
|
|
78
|
+
const subject = normalizeIdentityPart('subject', identity.subject);
|
|
79
|
+
return `${issuer}\u0000${subject}`;
|
|
80
|
+
}
|
|
81
|
+
export function billingMemberKey(input) {
|
|
82
|
+
return `${validateBillingAccountId(input.billingAccountId)}\u0000${billingIdentityKey(input)}`;
|
|
83
|
+
}
|
|
84
|
+
export function normalizeBillingIdentity(identity) {
|
|
85
|
+
return {
|
|
86
|
+
identityIssuer: normalizeIdentityPart('identityIssuer', identity.identityIssuer),
|
|
87
|
+
subject: normalizeIdentityPart('subject', identity.subject),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function normalizeBillingEmail(email) {
|
|
91
|
+
const value = email.trim().toLowerCase();
|
|
92
|
+
const parts = value.split('@');
|
|
93
|
+
const local = parts[0] ?? '';
|
|
94
|
+
const domain = parts[1] ?? '';
|
|
95
|
+
const validLocal = local.length > 0 &&
|
|
96
|
+
local.length <= 64 &&
|
|
97
|
+
!local.startsWith('.') &&
|
|
98
|
+
!local.endsWith('.') &&
|
|
99
|
+
!local.includes('..') &&
|
|
100
|
+
/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+$/.test(local);
|
|
101
|
+
const validDomain = domain.length > 0 &&
|
|
102
|
+
domain.length <= 255 &&
|
|
103
|
+
domain
|
|
104
|
+
.split('.')
|
|
105
|
+
.every((label) => label.length > 0 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label));
|
|
106
|
+
if (value.length > 320 || parts.length !== 2 || !validLocal || !validDomain) {
|
|
107
|
+
throw new Error('billing email must be a valid non-empty address');
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
export function validateBillingAccountId(value) {
|
|
112
|
+
if (!/^ba_[0-9a-f-]{36}$/.test(value))
|
|
113
|
+
throw new Error('invalid billing account id');
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
export function validateBillingLinkReason(value) {
|
|
117
|
+
return normalizeIdentityPart('billing link reason', value);
|
|
118
|
+
}
|
|
119
|
+
export function billingContentChecksum(value) {
|
|
120
|
+
return createHash('sha256').update(canonicalJsonFingerprint(value)).digest('hex');
|
|
121
|
+
}
|
|
122
|
+
function normalizeIdentityPart(name, value) {
|
|
123
|
+
const normalized = value.trim();
|
|
124
|
+
if (normalized.length === 0 ||
|
|
125
|
+
normalized.length > 512 ||
|
|
126
|
+
[...normalized].some((character) => {
|
|
127
|
+
const code = character.charCodeAt(0);
|
|
128
|
+
return code <= 0x1f || code === 0x7f;
|
|
129
|
+
})) {
|
|
130
|
+
throw new Error(`${name} must be a non-empty scalar identifier`);
|
|
131
|
+
}
|
|
132
|
+
return normalized;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/billing/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,yGAAyG;AACzG,MAAM,CAAC,MAAM,2BAA2B,GAAG,6BAA6B,CAAC;AAwEzE,MAAM,CAAC,MAAM,iBAAiB,GAAwB,MAAM,CAAC,MAAM,CAAC;IAClE,0BAA0B,EAAE,SAAS;IACrC,yBAAyB,EAAE,KAAK;IAChC,qBAAqB,EAAE,CAAC;IACxB,wBAAwB,EAAE,IAAI;IAC9B,oBAAoB,EAAE,KAAK;IAC3B,wBAAwB,EAAE,KAAK;IAC/B,yBAAyB,EAAE,OAAO;IAClC,iBAAiB,EAAE,OAAO;IAC1B,gBAAgB,EAAE,KAAK;IACvB,mBAAmB,EAAE,KAAK;IAC1B,kBAAkB,EAAE,KAAK;IACzB,eAAe,EAAE,WAAW;IAC5B,kBAAkB,EAAE,KAAK;IACzB,sBAAsB,EAAE,KAAK;IAC7B,sBAAsB,EAAE,KAAK;IAC7B,mBAAmB,EAAE,KAAK;IAC1B,sBAAsB,EAAE,KAAK;IAC7B,GAAG,EAAE,KAAK;CACX,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG;IACxB,EAAE,EAAE,QAAQ;IACZ,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,CAAC;IACV,aAAa,EAAE,0BAA0B;IACzC,YAAY,EAAE,iBAAiB;CAChC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAChE,GAAG,iBAAiB;IACpB,eAAe,EAAE,sBAAsB,CAAC,iBAAiB,CAAC;IAC1D,SAAS,EAAE,iBAAiB,CAAC,aAAa;CAC3C,CAAC,CAAC;AAEH,MAAM,UAAU,+BAA+B,CAAC,KAK/C;IAKC,MAAM,gBAAgB,GAAG,MAAM,UAAU,EAAE,EAAE,CAAC;IAC9C,MAAM,UAAU,GAAG,MAAM,UAAU,EAAE,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,OAAO,GAAyB;QACpC,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,KAAK;QAC/C,KAAK,EAAE,QAAQ;QACf,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;IACF,MAAM,MAAM,GAA+B;QACzC,gBAAgB;QAChB,GAAG,KAAK,CAAC,QAAQ;QACjB,KAAK;QACL,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;IACF,MAAM,eAAe,GAAG;QACtB,gBAAgB;QAChB,OAAO,EAAE,CAAC;QACV,aAAa,EAAE,iBAAiB,CAAC,EAAE;QACnC,aAAa,EAAE,KAAK,CAAC,SAAS;QAC9B,kBAAkB,EAAE,KAAK,CAAC,SAAS;QACnC,YAAY,EAAE,iBAAiB;KAChC,CAAC;IACF,MAAM,QAAQ,GAA8B;QAC1C,EAAE,EAAE,UAAU;QACd,GAAG,eAAe;QAClB,QAAQ,EAAE,iBAAiB,CAAC,QAAQ;QACpC,WAAW,EAAE,iBAAiB,CAAC,OAAO;QACtC,UAAU,EAAE,aAAa;QACzB,gBAAgB,EAAE,yBAAyB;QAC3C,kBAAkB,EAAE,UAAU;QAC9B,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAAC;QACxD,UAAU,EAAE,KAAK,CAAC,SAAS;QAC3B,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAA4B;IAC7D,MAAM,MAAM,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChF,MAAM,OAAO,GAAG,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnE,OAAO,GAAG,MAAM,SAAS,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAiE;IAEjE,OAAO,GAAG,wBAAwB,CAAC,KAAK,CAAC,gBAAgB,CAAC,SAAS,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,QAA4B;IACnE,OAAO;QACL,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,cAAc,CAAC;QAChF,OAAO,EAAE,qBAAqB,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,MAAM,UAAU,GACd,KAAK,CAAC,MAAM,GAAG,CAAC;QAChB,KAAK,CAAC,MAAM,IAAI,EAAE;QAClB,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QACtB,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QACpB,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QACrB,iCAAiC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,WAAW,GACf,MAAM,CAAC,MAAM,GAAG,CAAC;QACjB,MAAM,CAAC,MAAM,IAAI,GAAG;QACpB,MAAM;aACH,KAAK,CAAC,GAAG,CAAC;aACV,KAAK,CACJ,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,IAAI,mCAAmC,CAAC,IAAI,CAAC,KAAK,CAAC,CAC5F,CAAC;IACN,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACrF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,OAAO,qBAAqB,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAa;IACxD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAChC,IACE,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,UAAU,CAAC,MAAM,GAAG,GAAG;QACvB,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACrC,OAAO,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC;QACvC,CAAC,CAAC,EACF,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wCAAwC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type BillingAccountMemberRecord, type BillingAccountRecord, type EntitlementSnapshotRecord, type OrgBillingLinkRecord, type PlanVersionRecord } from './model.js';
|
|
2
|
+
export declare function accountRowToRecord(row: AccountRow): BillingAccountRecord;
|
|
3
|
+
export declare function memberRowToRecord(row: MemberRow): BillingAccountMemberRecord;
|
|
4
|
+
export declare function planVersionRowToRecord(row: PlanVersionRow): PlanVersionRecord;
|
|
5
|
+
export declare function entitlementSnapshotRowToRecord(row: EntitlementSnapshotRow): EntitlementSnapshotRecord;
|
|
6
|
+
export declare function orgBillingLinkRowToRecord(row: OrgBillingLinkRow): OrgBillingLinkRecord;
|
|
7
|
+
export declare function validateScalarId(name: string, value: string): string;
|
|
8
|
+
export interface AccountRow {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly display_name: string;
|
|
11
|
+
readonly state: string;
|
|
12
|
+
readonly created_at: Date | string;
|
|
13
|
+
}
|
|
14
|
+
export interface MemberRow {
|
|
15
|
+
readonly billing_account_id: string;
|
|
16
|
+
readonly identity_issuer: string;
|
|
17
|
+
readonly subject: string;
|
|
18
|
+
readonly email: string;
|
|
19
|
+
readonly role: string;
|
|
20
|
+
readonly created_at: Date | string;
|
|
21
|
+
}
|
|
22
|
+
export interface PlanVersionRow {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly plan_code: string;
|
|
25
|
+
readonly version: number;
|
|
26
|
+
readonly effective_from: Date | string;
|
|
27
|
+
readonly entitlements: unknown;
|
|
28
|
+
readonly content_checksum: string;
|
|
29
|
+
readonly created_at: Date | string;
|
|
30
|
+
}
|
|
31
|
+
export interface EntitlementSnapshotRow {
|
|
32
|
+
readonly id: string;
|
|
33
|
+
readonly billing_account_id: string;
|
|
34
|
+
readonly version: number;
|
|
35
|
+
readonly plan_version_id: string;
|
|
36
|
+
readonly plan_code: string;
|
|
37
|
+
readonly plan_version: number;
|
|
38
|
+
readonly plan_effective_from: Date | string;
|
|
39
|
+
readonly plan_entitlements: unknown;
|
|
40
|
+
readonly plan_content_checksum: string;
|
|
41
|
+
readonly plan_created_at: Date | string;
|
|
42
|
+
readonly source_kind: string;
|
|
43
|
+
readonly generation_reason: string;
|
|
44
|
+
readonly verification_status: string;
|
|
45
|
+
readonly effective_from: Date | string;
|
|
46
|
+
readonly monthly_usage_anchor: Date | string;
|
|
47
|
+
readonly entitlements: unknown;
|
|
48
|
+
readonly content_checksum: string;
|
|
49
|
+
readonly verified_at: Date | string;
|
|
50
|
+
readonly created_at: Date | string;
|
|
51
|
+
}
|
|
52
|
+
export interface OrgBillingLinkRow {
|
|
53
|
+
readonly org_slug: string;
|
|
54
|
+
readonly billing_account_id: string;
|
|
55
|
+
readonly home_free_billing_account_id: string;
|
|
56
|
+
readonly home_owner_identity_issuer: string;
|
|
57
|
+
readonly home_owner_subject: string;
|
|
58
|
+
readonly fallback_billing_account_id: string;
|
|
59
|
+
readonly version: number;
|
|
60
|
+
readonly active: boolean;
|
|
61
|
+
readonly linked_at: Date | string;
|
|
62
|
+
readonly linked_by_identity_issuer: string;
|
|
63
|
+
readonly linked_by_subject: string;
|
|
64
|
+
readonly reason: string;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=postgres-records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-records.d.ts","sourceRoot":"","sources":["../../src/billing/postgres-records.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { validateSlug } from '../store/validate.js';
|
|
2
|
+
import { billingContentChecksum, normalizeBillingEmail, normalizeBillingIdentity, validateBillingAccountId, } from './model.js';
|
|
3
|
+
export function accountRowToRecord(row) {
|
|
4
|
+
if (row.state !== 'active' && row.state !== 'draft' && row.state !== 'inactive') {
|
|
5
|
+
throw new Error('persisted billing account has an invalid state');
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
id: validateBillingAccountId(row.id),
|
|
9
|
+
displayName: row.display_name,
|
|
10
|
+
state: row.state,
|
|
11
|
+
createdAt: toIsoString(row.created_at),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function memberRowToRecord(row) {
|
|
15
|
+
if (row.role !== 'owner' && row.role !== 'admin' && row.role !== 'viewer') {
|
|
16
|
+
throw new Error('persisted billing member has an invalid role');
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
billingAccountId: validateBillingAccountId(row.billing_account_id),
|
|
20
|
+
...normalizeBillingIdentity({
|
|
21
|
+
identityIssuer: row.identity_issuer,
|
|
22
|
+
subject: row.subject,
|
|
23
|
+
}),
|
|
24
|
+
email: normalizeBillingEmail(row.email),
|
|
25
|
+
role: row.role,
|
|
26
|
+
createdAt: toIsoString(row.created_at),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function planVersionRowToRecord(row) {
|
|
30
|
+
const effectiveFrom = toIsoString(row.effective_from);
|
|
31
|
+
const entitlements = parseEntitlements(row.entitlements);
|
|
32
|
+
const record = {
|
|
33
|
+
id: validateScalarId('plan version id', row.id),
|
|
34
|
+
planCode: validateScalarId('plan code', row.plan_code),
|
|
35
|
+
version: validatePositiveInteger('plan version', row.version),
|
|
36
|
+
effectiveFrom,
|
|
37
|
+
entitlements,
|
|
38
|
+
contentChecksum: row.content_checksum,
|
|
39
|
+
createdAt: toIsoString(row.created_at),
|
|
40
|
+
};
|
|
41
|
+
const checksum = billingContentChecksum({
|
|
42
|
+
id: record.id,
|
|
43
|
+
planCode: record.planCode,
|
|
44
|
+
version: record.version,
|
|
45
|
+
effectiveFrom: record.effectiveFrom,
|
|
46
|
+
entitlements: record.entitlements,
|
|
47
|
+
});
|
|
48
|
+
if (checksum !== record.contentChecksum) {
|
|
49
|
+
throw new Error(`persisted plan version ${record.id} failed checksum verification`);
|
|
50
|
+
}
|
|
51
|
+
return record;
|
|
52
|
+
}
|
|
53
|
+
export function entitlementSnapshotRowToRecord(row) {
|
|
54
|
+
if (row.source_kind !== 'system-free' ||
|
|
55
|
+
row.generation_reason !== 'default-account-created' ||
|
|
56
|
+
row.verification_status !== 'verified') {
|
|
57
|
+
throw new Error('persisted entitlement snapshot has an unsupported provenance');
|
|
58
|
+
}
|
|
59
|
+
const effectiveFrom = toIsoString(row.effective_from);
|
|
60
|
+
const monthlyUsageAnchor = toIsoString(row.monthly_usage_anchor);
|
|
61
|
+
const entitlements = parseEntitlements(row.entitlements);
|
|
62
|
+
const plan = planVersionRowToRecord({
|
|
63
|
+
id: row.plan_version_id,
|
|
64
|
+
plan_code: row.plan_code,
|
|
65
|
+
version: row.plan_version,
|
|
66
|
+
effective_from: row.plan_effective_from,
|
|
67
|
+
entitlements: row.plan_entitlements,
|
|
68
|
+
content_checksum: row.plan_content_checksum,
|
|
69
|
+
created_at: row.plan_created_at,
|
|
70
|
+
});
|
|
71
|
+
const record = {
|
|
72
|
+
id: validateScalarId('entitlement snapshot id', row.id),
|
|
73
|
+
billingAccountId: validateBillingAccountId(row.billing_account_id),
|
|
74
|
+
version: validatePositiveInteger('entitlement snapshot version', row.version),
|
|
75
|
+
planVersionId: validateScalarId('plan version id', row.plan_version_id),
|
|
76
|
+
planCode: plan.planCode,
|
|
77
|
+
planVersion: plan.version,
|
|
78
|
+
sourceKind: row.source_kind,
|
|
79
|
+
generationReason: row.generation_reason,
|
|
80
|
+
verificationStatus: row.verification_status,
|
|
81
|
+
effectiveFrom,
|
|
82
|
+
monthlyUsageAnchor,
|
|
83
|
+
entitlements,
|
|
84
|
+
contentChecksum: row.content_checksum,
|
|
85
|
+
verifiedAt: toIsoString(row.verified_at),
|
|
86
|
+
createdAt: toIsoString(row.created_at),
|
|
87
|
+
};
|
|
88
|
+
const checksum = billingContentChecksum({
|
|
89
|
+
billingAccountId: record.billingAccountId,
|
|
90
|
+
version: record.version,
|
|
91
|
+
planVersionId: record.planVersionId,
|
|
92
|
+
effectiveFrom: record.effectiveFrom,
|
|
93
|
+
monthlyUsageAnchor: record.monthlyUsageAnchor,
|
|
94
|
+
entitlements: record.entitlements,
|
|
95
|
+
});
|
|
96
|
+
if (checksum !== record.contentChecksum) {
|
|
97
|
+
throw new Error(`persisted entitlement snapshot ${record.id} failed checksum verification`);
|
|
98
|
+
}
|
|
99
|
+
return record;
|
|
100
|
+
}
|
|
101
|
+
export function orgBillingLinkRowToRecord(row) {
|
|
102
|
+
if (!row.active)
|
|
103
|
+
throw new Error('persisted current billing-account link is inactive');
|
|
104
|
+
return {
|
|
105
|
+
orgSlug: validateSlug('org', row.org_slug),
|
|
106
|
+
billingAccountId: validateBillingAccountId(row.billing_account_id),
|
|
107
|
+
homeFreeBillingAccountId: validateBillingAccountId(row.home_free_billing_account_id),
|
|
108
|
+
homeOwner: normalizeBillingIdentity({
|
|
109
|
+
identityIssuer: row.home_owner_identity_issuer,
|
|
110
|
+
subject: row.home_owner_subject,
|
|
111
|
+
}),
|
|
112
|
+
fallbackBillingAccountId: validateBillingAccountId(row.fallback_billing_account_id),
|
|
113
|
+
version: validatePositiveInteger('billing link version', row.version),
|
|
114
|
+
active: true,
|
|
115
|
+
linkedAt: toIsoString(row.linked_at),
|
|
116
|
+
linkedBy: normalizeBillingIdentity({
|
|
117
|
+
identityIssuer: row.linked_by_identity_issuer,
|
|
118
|
+
subject: row.linked_by_subject,
|
|
119
|
+
}),
|
|
120
|
+
reason: validateScalarId('billing link reason', row.reason),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function parseEntitlements(value) {
|
|
124
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
125
|
+
throw new Error('persisted entitlements must be an object');
|
|
126
|
+
}
|
|
127
|
+
const entitlements = {};
|
|
128
|
+
for (const [key, candidate] of Object.entries(value)) {
|
|
129
|
+
validateScalarId('entitlement key', key);
|
|
130
|
+
if (candidate !== null &&
|
|
131
|
+
typeof candidate !== 'boolean' &&
|
|
132
|
+
typeof candidate !== 'string' &&
|
|
133
|
+
(typeof candidate !== 'number' || !Number.isFinite(candidate))) {
|
|
134
|
+
throw new Error(`persisted entitlement ${key} has an invalid value`);
|
|
135
|
+
}
|
|
136
|
+
entitlements[key] = candidate;
|
|
137
|
+
}
|
|
138
|
+
return Object.freeze(entitlements);
|
|
139
|
+
}
|
|
140
|
+
function validatePositiveInteger(name, value) {
|
|
141
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
142
|
+
throw new Error(`${name} must be positive`);
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
export function validateScalarId(name, value) {
|
|
146
|
+
const normalized = value.trim();
|
|
147
|
+
if (normalized.length === 0 ||
|
|
148
|
+
normalized.length > 512 ||
|
|
149
|
+
[...normalized].some((character) => {
|
|
150
|
+
const code = character.charCodeAt(0);
|
|
151
|
+
return code <= 0x1f || code === 0x7f;
|
|
152
|
+
})) {
|
|
153
|
+
throw new Error(`${name} must be a non-empty scalar identifier`);
|
|
154
|
+
}
|
|
155
|
+
return normalized;
|
|
156
|
+
}
|
|
157
|
+
function toIsoString(value) {
|
|
158
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
159
|
+
if (!Number.isFinite(date.getTime()))
|
|
160
|
+
throw new Error('persisted billing timestamp is invalid');
|
|
161
|
+
return date.toISOString();
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=postgres-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-records.js","sourceRoot":"","sources":["../../src/billing/postgres-records.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-schema.d.ts","sourceRoot":"","sources":["../../src/billing/postgres-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAI/B,2FAA2F;AAC3F,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAwJnE"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { FREE_PLAN_VERSION } from './model.js';
|
|
2
|
+
import { planVersionRowToRecord } from './postgres-records.js';
|
|
3
|
+
/** Additive BA-1 schema. No existing organization is linked or otherwise migrated here. */
|
|
4
|
+
export async function ensureBillingSchema(pool) {
|
|
5
|
+
await pool.query(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS billing_plan_versions (
|
|
7
|
+
id text PRIMARY KEY,
|
|
8
|
+
plan_code text NOT NULL,
|
|
9
|
+
version integer NOT NULL CHECK (version > 0),
|
|
10
|
+
effective_from timestamptz NOT NULL,
|
|
11
|
+
entitlements jsonb NOT NULL,
|
|
12
|
+
content_checksum text NOT NULL,
|
|
13
|
+
created_at timestamptz NOT NULL,
|
|
14
|
+
UNIQUE (plan_code, version)
|
|
15
|
+
)
|
|
16
|
+
`);
|
|
17
|
+
await pool.query(`
|
|
18
|
+
CREATE TABLE IF NOT EXISTS billing_accounts (
|
|
19
|
+
id text PRIMARY KEY,
|
|
20
|
+
display_name text NOT NULL,
|
|
21
|
+
state text NOT NULL CHECK (state IN ('active', 'draft', 'inactive')),
|
|
22
|
+
created_at timestamptz NOT NULL
|
|
23
|
+
)
|
|
24
|
+
`);
|
|
25
|
+
await pool.query(`
|
|
26
|
+
CREATE TABLE IF NOT EXISTS billing_account_defaults (
|
|
27
|
+
identity_issuer text NOT NULL,
|
|
28
|
+
subject text NOT NULL,
|
|
29
|
+
billing_account_id text NOT NULL UNIQUE REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
30
|
+
PRIMARY KEY (identity_issuer, subject)
|
|
31
|
+
)
|
|
32
|
+
`);
|
|
33
|
+
await pool.query(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS billing_account_members (
|
|
35
|
+
billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
36
|
+
identity_issuer text NOT NULL,
|
|
37
|
+
subject text NOT NULL,
|
|
38
|
+
email text NOT NULL,
|
|
39
|
+
role text NOT NULL CHECK (role IN ('owner', 'admin', 'viewer')),
|
|
40
|
+
created_at timestamptz NOT NULL,
|
|
41
|
+
PRIMARY KEY (billing_account_id, identity_issuer, subject)
|
|
42
|
+
)
|
|
43
|
+
`);
|
|
44
|
+
await pool.query(`
|
|
45
|
+
CREATE UNIQUE INDEX IF NOT EXISTS billing_account_members_default_owner_key
|
|
46
|
+
ON billing_account_members (identity_issuer, subject, billing_account_id)
|
|
47
|
+
`);
|
|
48
|
+
await pool.query(`
|
|
49
|
+
CREATE UNIQUE INDEX IF NOT EXISTS billing_account_defaults_owner_account_key
|
|
50
|
+
ON billing_account_defaults (identity_issuer, subject, billing_account_id)
|
|
51
|
+
`);
|
|
52
|
+
await pool.query(`
|
|
53
|
+
DO $billing_default_owner$
|
|
54
|
+
BEGIN
|
|
55
|
+
IF NOT EXISTS (
|
|
56
|
+
SELECT 1
|
|
57
|
+
FROM pg_constraint
|
|
58
|
+
WHERE conname = 'billing_account_defaults_owner_member_fk'
|
|
59
|
+
AND conrelid = 'billing_account_defaults'::regclass
|
|
60
|
+
) THEN
|
|
61
|
+
BEGIN
|
|
62
|
+
ALTER TABLE billing_account_defaults
|
|
63
|
+
ADD CONSTRAINT billing_account_defaults_owner_member_fk
|
|
64
|
+
FOREIGN KEY (identity_issuer, subject, billing_account_id)
|
|
65
|
+
REFERENCES billing_account_members (identity_issuer, subject, billing_account_id)
|
|
66
|
+
ON DELETE RESTRICT;
|
|
67
|
+
EXCEPTION WHEN duplicate_object THEN
|
|
68
|
+
NULL;
|
|
69
|
+
END;
|
|
70
|
+
END IF;
|
|
71
|
+
END
|
|
72
|
+
$billing_default_owner$
|
|
73
|
+
`);
|
|
74
|
+
await pool.query(`
|
|
75
|
+
CREATE TABLE IF NOT EXISTS billing_entitlement_snapshots (
|
|
76
|
+
id text PRIMARY KEY,
|
|
77
|
+
billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
78
|
+
version integer NOT NULL CHECK (version > 0),
|
|
79
|
+
plan_version_id text NOT NULL REFERENCES billing_plan_versions(id) ON DELETE RESTRICT,
|
|
80
|
+
source_kind text NOT NULL CHECK (source_kind IN ('system-free')),
|
|
81
|
+
generation_reason text NOT NULL CHECK (generation_reason IN ('default-account-created')),
|
|
82
|
+
verification_status text NOT NULL CHECK (verification_status IN ('verified')),
|
|
83
|
+
effective_from timestamptz NOT NULL,
|
|
84
|
+
monthly_usage_anchor timestamptz NOT NULL,
|
|
85
|
+
entitlements jsonb NOT NULL,
|
|
86
|
+
content_checksum text NOT NULL,
|
|
87
|
+
verified_at timestamptz NOT NULL,
|
|
88
|
+
created_at timestamptz NOT NULL,
|
|
89
|
+
UNIQUE (billing_account_id, version),
|
|
90
|
+
UNIQUE (billing_account_id, id)
|
|
91
|
+
)
|
|
92
|
+
`);
|
|
93
|
+
await pool.query(`
|
|
94
|
+
CREATE TABLE IF NOT EXISTS billing_account_active_snapshots (
|
|
95
|
+
billing_account_id text PRIMARY KEY REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
96
|
+
snapshot_id text NOT NULL UNIQUE,
|
|
97
|
+
FOREIGN KEY (billing_account_id, snapshot_id)
|
|
98
|
+
REFERENCES billing_entitlement_snapshots(billing_account_id, id) ON DELETE RESTRICT
|
|
99
|
+
)
|
|
100
|
+
`);
|
|
101
|
+
await pool.query(`
|
|
102
|
+
CREATE TABLE IF NOT EXISTS org_billing_home_accounts (
|
|
103
|
+
org_slug text PRIMARY KEY REFERENCES orgs(slug) ON DELETE RESTRICT,
|
|
104
|
+
billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
105
|
+
home_owner_identity_issuer text NOT NULL,
|
|
106
|
+
home_owner_subject text NOT NULL,
|
|
107
|
+
UNIQUE (org_slug, billing_account_id),
|
|
108
|
+
FOREIGN KEY (home_owner_identity_issuer, home_owner_subject, billing_account_id)
|
|
109
|
+
REFERENCES billing_account_defaults(identity_issuer, subject, billing_account_id)
|
|
110
|
+
ON DELETE RESTRICT
|
|
111
|
+
)
|
|
112
|
+
`);
|
|
113
|
+
await pool.query(`
|
|
114
|
+
CREATE TABLE IF NOT EXISTS org_billing_links (
|
|
115
|
+
org_slug text PRIMARY KEY REFERENCES orgs(slug) ON DELETE RESTRICT,
|
|
116
|
+
billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
117
|
+
home_free_billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
118
|
+
fallback_billing_account_id text NOT NULL REFERENCES billing_accounts(id) ON DELETE RESTRICT,
|
|
119
|
+
version integer NOT NULL CHECK (version > 0),
|
|
120
|
+
active boolean NOT NULL DEFAULT true CHECK (active),
|
|
121
|
+
linked_at timestamptz NOT NULL,
|
|
122
|
+
linked_by_identity_issuer text NOT NULL,
|
|
123
|
+
linked_by_subject text NOT NULL,
|
|
124
|
+
reason text NOT NULL,
|
|
125
|
+
FOREIGN KEY (org_slug, home_free_billing_account_id)
|
|
126
|
+
REFERENCES org_billing_home_accounts(org_slug, billing_account_id) ON DELETE RESTRICT
|
|
127
|
+
)
|
|
128
|
+
`);
|
|
129
|
+
await pool.query(`INSERT INTO billing_plan_versions
|
|
130
|
+
(id, plan_code, version, effective_from, entitlements, content_checksum, created_at)
|
|
131
|
+
VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7)
|
|
132
|
+
ON CONFLICT (id) DO NOTHING`, [
|
|
133
|
+
FREE_PLAN_VERSION.id,
|
|
134
|
+
FREE_PLAN_VERSION.planCode,
|
|
135
|
+
FREE_PLAN_VERSION.version,
|
|
136
|
+
FREE_PLAN_VERSION.effectiveFrom,
|
|
137
|
+
JSON.stringify(FREE_PLAN_VERSION.entitlements),
|
|
138
|
+
FREE_PLAN_VERSION.contentChecksum,
|
|
139
|
+
FREE_PLAN_VERSION.createdAt,
|
|
140
|
+
]);
|
|
141
|
+
const { rows } = await pool.query('SELECT * FROM billing_plan_versions WHERE id = $1', [FREE_PLAN_VERSION.id]);
|
|
142
|
+
const persistedFree = rows[0] ? planVersionRowToRecord(rows[0]) : undefined;
|
|
143
|
+
if (persistedFree === undefined ||
|
|
144
|
+
persistedFree.contentChecksum !== FREE_PLAN_VERSION.contentChecksum) {
|
|
145
|
+
throw new Error('persisted Free plan version does not match the immutable platform definition');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=postgres-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-schema.js","sourceRoot":"","sources":["../../src/billing/postgres-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAuB,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpF,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAU;IAClD,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;GAWhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;GAUhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;GAGhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;GAqBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;GAkBhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;GAOhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;GAWhB,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;GAehB,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,KAAK,CACd;;;iCAG6B,EAC7B;QACE,iBAAiB,CAAC,EAAE;QACpB,iBAAiB,CAAC,QAAQ;QAC1B,iBAAiB,CAAC,OAAO;QACzB,iBAAiB,CAAC,aAAa;QAC/B,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,YAAY,CAAC;QAC9C,iBAAiB,CAAC,eAAe;QACjC,iBAAiB,CAAC,SAAS;KAC5B,CACF,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAC/B,mDAAmD,EACnD,CAAC,iBAAiB,CAAC,EAAE,CAAC,CACvB,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,CAAC,eAAe,KAAK,iBAAiB,CAAC,eAAe,EACnE,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;AACH,CAAC"}
|