@farthershore/product 0.0.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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,244 @@
1
+ import type { CacheProfile, ActionSpecJson, CapabilityFileJson, FrontendComponentId, FrontendGateMode, FrontendManifestJson, GrantJson, ManifestBuildResult, MeterDefinitionJson, MigrationDeclJson, MigrationPinJson, MutationClass, CountedResourceJson, PlanLimitJson, PlanMeterJson, PlanSpecJson, PolicyFileJson, RoutesFileJson } from "./ir-types.js";
2
+ import type { PriceSpec } from "./price.js";
3
+ /** Brand symbol so the bin can recognize a Business across SDK copies
4
+ * (the repo's pinned SDK vs whatever loaded the entry). */
5
+ export declare const BUSINESS_BRAND: unique symbol;
6
+ export type BusinessOptions = {
7
+ /** Upstream origin the gateway proxies to. */
8
+ baseUrl: string;
9
+ displayName?: string;
10
+ description?: string;
11
+ sandboxBaseUrl?: string;
12
+ visibility?: "public" | "private";
13
+ logoUrl?: string;
14
+ primaryColor?: string;
15
+ envBranchPrefix?: string | null;
16
+ /** API-key header the gateway reads (default x-api-key). */
17
+ authHeader?: string;
18
+ upstreamAuth?: {
19
+ type: "none" | "static_bearer";
20
+ token?: string;
21
+ };
22
+ billOn4xx?: boolean;
23
+ billing?: {
24
+ gracePeriodDays?: number;
25
+ applyLimitUpgradesInstantly?: boolean;
26
+ };
27
+ };
28
+ export type MeterOptions = Omit<MeterDefinitionJson, "key">;
29
+ export type CapabilityOptions = {
30
+ title?: string;
31
+ description?: string;
32
+ mutationClass?: MutationClass;
33
+ includesFeatures?: Array<string | FeatureRef>;
34
+ includesPolicies?: Array<string | PolicyRef>;
35
+ includesCapabilities?: Array<string | CapabilityRef>;
36
+ };
37
+ export type RouteOptions = {
38
+ /** Meter keys this route increments. Omitted = all product meters. */
39
+ meters?: Array<string | MeterRef>;
40
+ unmetered?: boolean;
41
+ action?: string | ActionRef;
42
+ };
43
+ export type ActionOptions = Omit<ActionSpecJson, "id">;
44
+ export type FrontendNavItemOptions = {
45
+ label: string;
46
+ path: string;
47
+ capability?: string | CapabilityRef;
48
+ };
49
+ export type FrontendComponentOptions = {
50
+ component: FrontendComponentId;
51
+ props?: Record<string, unknown>;
52
+ capability?: string | CapabilityRef;
53
+ gateMode?: FrontendGateMode;
54
+ };
55
+ export type FrontendPageOptions = {
56
+ title: string;
57
+ requiresAuth: boolean;
58
+ capability?: string | CapabilityRef;
59
+ components?: FrontendComponentOptions[];
60
+ };
61
+ export type MigrationPlanRefOptions = {
62
+ plan: string | PlanRef;
63
+ version?: string;
64
+ };
65
+ export type MigrationTargetRefOptions = {
66
+ plan: string | PlanRef;
67
+ version?: "head";
68
+ };
69
+ export type MigrationPinOptions = Omit<MigrationPinJson, "pinTo"> & {
70
+ pinTo: {
71
+ plan: string | PlanRef;
72
+ version: string;
73
+ };
74
+ };
75
+ export type MigrationOptions = Omit<MigrationDeclJson, "id" | "from" | "to" | "newCustomers" | "pins"> & {
76
+ from: MigrationPlanRefOptions;
77
+ to: MigrationTargetRefOptions;
78
+ newCustomers?: "immediate";
79
+ pins?: MigrationPinOptions[];
80
+ };
81
+ export type ResourceOptions = Omit<CountedResourceJson, "name">;
82
+ export type FeatureOptions = {
83
+ description?: string;
84
+ mutationClass?: MutationClass;
85
+ cacheProfile?: CacheProfile;
86
+ /** Per-feature upstream origin override (default: product baseUrl). */
87
+ upstreamOrigin?: string | null;
88
+ policies?: Array<string | PolicyRef>;
89
+ capabilities?: Array<string | CapabilityRef>;
90
+ /** Plans that grant this feature directly. */
91
+ plans?: Array<string | PlanRef>;
92
+ rolloutKey?: string;
93
+ requiredFlags?: string[];
94
+ actions?: Array<{
95
+ id: string;
96
+ } & ActionOptions>;
97
+ /** Routes, e.g. `{ match: "POST /v1/jobs", meters: ["requests"] }`. */
98
+ routes?: Array<{
99
+ match: string;
100
+ } & RouteOptions>;
101
+ };
102
+ export type PolicyOptions = {
103
+ type: string;
104
+ config: Record<string, unknown>;
105
+ description?: string;
106
+ mutationClass?: MutationClass;
107
+ cacheProfile?: CacheProfile;
108
+ compatibleWith?: {
109
+ routeTypes?: string[];
110
+ meters?: Array<string | MeterRef>;
111
+ authModes?: string[];
112
+ };
113
+ };
114
+ export type PlanOptions = {
115
+ name: string;
116
+ description?: string;
117
+ details?: string[];
118
+ /** From fs.price.monthly/yearly/free. Omit for a 0-fee plan. */
119
+ price?: PriceSpec;
120
+ /** Per-dimension metered pricing (platform 5-knob meters[] shape). */
121
+ meters?: PlanMeterJson[];
122
+ /** Credit grants and/or capability enablements from `cap.enable()`. */
123
+ grants?: Array<GrantJson | PlanCapabilityGrant>;
124
+ trialDays?: number;
125
+ maxMonthlySpendCents?: number;
126
+ minMonthlySpendCents?: number;
127
+ limits?: PlanLimitJson[];
128
+ featureGates?: Record<string, boolean>;
129
+ capabilityLimits?: Record<string, number | boolean>;
130
+ /** Capability keys granted without per-capability limits. */
131
+ capabilities?: Array<string | CapabilityRef>;
132
+ overageBehavior?: "block" | "allow_and_bill";
133
+ selfServeEnabled?: boolean;
134
+ legacy?: boolean;
135
+ archive?: {
136
+ at?: string;
137
+ transitionTo?: string;
138
+ strategy?: "auto" | "explicit" | "block";
139
+ };
140
+ /** Escape hatch: merged onto the emitted plan spec last (e.g. variants). */
141
+ raw?: Record<string, unknown>;
142
+ };
143
+ export type MeterRef = {
144
+ readonly kind: "meter";
145
+ readonly key: string;
146
+ };
147
+ export type ResourceRef = {
148
+ readonly kind: "resource";
149
+ readonly key: string;
150
+ };
151
+ export type PolicyRef = {
152
+ readonly kind: "policy";
153
+ readonly key: string;
154
+ };
155
+ export type PlanRef = {
156
+ readonly kind: "plan";
157
+ readonly key: string;
158
+ };
159
+ export type ActionRef = {
160
+ readonly kind: "action";
161
+ readonly key: string;
162
+ };
163
+ export type FeatureRef = {
164
+ readonly kind: "feature";
165
+ readonly key: string;
166
+ /** Declare an action co-located with this feature. */
167
+ action(id: string, options: ActionOptions): ActionRef;
168
+ /** Append a route to this feature: `feature.route("POST /v1/x", {...})`. */
169
+ route(match: string, options?: RouteOptions): FeatureRef;
170
+ };
171
+ export type CapabilityRef = {
172
+ readonly kind: "capability";
173
+ readonly key: string;
174
+ /** Grant this capability on a plan, optionally with numeric/boolean
175
+ * capability limits: `cap.enable({ limits: { cron_jobs: 10 } })`. */
176
+ enable(options?: {
177
+ limits?: Record<string, number | boolean>;
178
+ }): PlanCapabilityGrant;
179
+ };
180
+ export type PlanCapabilityGrant = {
181
+ readonly kind: "capability_grant";
182
+ readonly capability: string;
183
+ readonly limits?: Record<string, number | boolean>;
184
+ };
185
+ export declare class Business {
186
+ readonly [BUSINESS_BRAND] = true;
187
+ readonly name: string;
188
+ private readonly options;
189
+ private readonly meters;
190
+ private readonly resources;
191
+ private readonly plans;
192
+ private readonly features;
193
+ private readonly policies;
194
+ private readonly capabilities;
195
+ private migrations;
196
+ private frontendManifest?;
197
+ private productPatch;
198
+ /** Sugar for binding API routes to features. */
199
+ readonly api: {
200
+ route: (match: string, options: {
201
+ feature: string | FeatureRef;
202
+ } & RouteOptions) => Business;
203
+ };
204
+ readonly frontend: {
205
+ nav: (items: FrontendNavItemOptions[]) => Business;
206
+ page: (path: string, options: FrontendPageOptions) => Business;
207
+ manifest: (manifest: FrontendManifestJson) => Business;
208
+ };
209
+ readonly lifecycle: {
210
+ migration: (id: string, options: MigrationOptions) => Business;
211
+ migrations: (migrations: MigrationDeclJson[]) => Business;
212
+ };
213
+ /** Escape hatches — raw platform-schema JSON, validated at toIR(). */
214
+ readonly raw: {
215
+ /** Deep-merged onto the emitted product spec (usage, webhooks,
216
+ * environments, add_ons, lifecycle, billing overrides, …). */
217
+ productPatch: (patch: Record<string, unknown>) => Business;
218
+ plan: (spec: PlanSpecJson) => Business;
219
+ routesFile: (file: RoutesFileJson) => Business;
220
+ policyFile: (file: PolicyFileJson) => Business;
221
+ capabilityFile: (file: CapabilityFileJson) => Business;
222
+ frontend: (manifest: FrontendManifestJson) => Business;
223
+ };
224
+ constructor(name: string, options: BusinessOptions);
225
+ meter(key: string, options: MeterOptions): MeterRef;
226
+ resource(name: string, options?: ResourceOptions): ResourceRef;
227
+ capability(key: string, options?: CapabilityOptions): CapabilityRef;
228
+ feature(key: string, options?: FeatureOptions): FeatureRef;
229
+ policy(name: string, options: PolicyOptions): PolicyRef;
230
+ plan(key: string, options: PlanOptions): PlanRef;
231
+ /** Assemble + validate the Manifest IR. Throws ManifestValidationError
232
+ * with structured issues when the declared state is invalid. */
233
+ toIR(): ManifestBuildResult;
234
+ private buildProductSpec;
235
+ private buildRoute;
236
+ private ensureFrontendManifest;
237
+ private normalizeMigrationPlanRef;
238
+ private normalizeMigrationTargetRef;
239
+ private assertUniqueMigrationId;
240
+ private assertUniqueMigrationIds;
241
+ private assertNewKey;
242
+ }
243
+ export declare function isBusiness(value: unknown): value is Business;
244
+ export declare function business(name: string, options: BusinessOptions): Business;
@@ -0,0 +1,2 @@
1
+ import type { ManifestIrDocument } from "../ir-types.js";
2
+ export declare function generateManifestSource(ir: ManifestIrDocument): string;
@@ -0,0 +1,19 @@
1
+ /** One validation problem, in the platform's diagnostic envelope shape. */
2
+ export type ManifestIssue = {
3
+ code: string;
4
+ path: string;
5
+ message: string;
6
+ };
7
+ /**
8
+ * Thrown by `business.toIR()` (and the manifest-build bin) when the
9
+ * assembled manifest fails schema validation. Plain-data `issues` so the
10
+ * error survives serialization across the runner callback boundary.
11
+ */
12
+ export declare class ManifestValidationError extends Error {
13
+ readonly issues: ManifestIssue[];
14
+ constructor(issues: ManifestIssue[]);
15
+ }
16
+ /** Thrown on builder misuse (duplicate keys, unknown refs) at call time. */
17
+ export declare class ManifestBuilderError extends Error {
18
+ constructor(message: string);
19
+ }
@@ -0,0 +1,21 @@
1
+ import { business } from "./business.js";
2
+ export declare const product: typeof business;
3
+ export declare const fs: {
4
+ business: typeof business;
5
+ product: typeof business;
6
+ price: {
7
+ monthly(dollars: number): import("./price.js").PriceSpec;
8
+ yearly(dollars: number): import("./price.js").PriceSpec;
9
+ free(): import("./price.js").PriceSpec;
10
+ };
11
+ };
12
+ export { business, Business, isBusiness, BUSINESS_BRAND } from "./business.js";
13
+ export { price } from "./price.js";
14
+ export { validateManifestIr, hashIr, canonicalIrJson } from "./validate.js";
15
+ export { ManifestValidationError, ManifestBuilderError } from "./errors.js";
16
+ export { SDK_VERSION } from "./version.js";
17
+ export type { BusinessOptions, MeterOptions, CapabilityOptions, FrontendNavItemOptions, FrontendPageOptions, FrontendComponentOptions, FeatureOptions, RouteOptions, PolicyOptions, PlanOptions, MeterRef, PolicyRef, PlanRef, FeatureRef, CapabilityRef, PlanCapabilityGrant, } from "./business.js";
18
+ export type { PriceSpec } from "./price.js";
19
+ export type { ManifestIssue } from "./errors.js";
20
+ export type { ValidationResult } from "./validate.js";
21
+ export type { ManifestIrDocument, ManifestBuildResult, ProductSpecJson, PlanSpecJson, RoutesFileJson, PolicyFileJson, CapabilityFileJson, FrontendManifestJson, FrontendNavItemJson, FrontendPageJson, FrontendComponentJson, FrontendComponentId, FrontendGateMode, MeterDefinitionJson, PlanMeterJson, PlanLimitJson, GrantJson, HttpMethod, MutationClass, CacheProfile, } from "./ir-types.js";
@@ -0,0 +1,250 @@
1
+ export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "*";
2
+ export type MutationClass = "runtime" | "contractual";
3
+ export type CacheProfile = "long" | "short" | "blocking";
4
+ export type MeterDefinitionJson = {
5
+ key: string;
6
+ display: string;
7
+ unit?: string;
8
+ enforcementType?: "exact_pre_request" | "estimated_then_settled" | "postpaid" | "strict_concurrency";
9
+ aggregation?: "SUM" | "COUNT" | "MAX" | "UNIQUE_COUNT" | "LATEST";
10
+ window?: "minute" | "hour" | "day" | "month" | "billing_period";
11
+ valueProperty?: string;
12
+ uniqueProperty?: string;
13
+ groupBy?: string[];
14
+ eventCode?: string;
15
+ };
16
+ /** Per-plan meter pricing (the 5-knob `meters[]` entry). Field set follows
17
+ * the platform schema; extra fields pass through to validation. */
18
+ export type PlanMeterJson = {
19
+ dimension: string;
20
+ [key: string]: unknown;
21
+ };
22
+ export type GrantJson = {
23
+ kind: "recurring" | "one_time" | "promotional" | "trial" | "rollover" | "top_up" | "auto_recharge";
24
+ [key: string]: unknown;
25
+ };
26
+ export type PlanLimitWindowJson = {
27
+ type: "named";
28
+ name: "second" | "minute" | "hour" | "day" | "week" | "month";
29
+ } | {
30
+ type: "custom";
31
+ seconds: number;
32
+ label?: string;
33
+ };
34
+ export type PlanLimitJson = {
35
+ dimension: string;
36
+ window: PlanLimitWindowJson;
37
+ capacity: number;
38
+ enforcement?: "enforce" | "track";
39
+ reset_trigger?: "billing_period" | "subscription_start";
40
+ };
41
+ export type PlanArchiveJson = {
42
+ at?: string;
43
+ transitionTo?: string;
44
+ strategy?: "auto" | "explicit" | "block";
45
+ };
46
+ export type PlanSpecJson = {
47
+ key: string;
48
+ name: string;
49
+ description?: string;
50
+ details?: string[];
51
+ meters?: PlanMeterJson[];
52
+ recurring_fee_cents?: number;
53
+ billing_interval?: "month" | "year";
54
+ trial_days?: number;
55
+ max_monthly_spend_cents?: number;
56
+ min_monthly_spend_cents?: number;
57
+ grants?: GrantJson[];
58
+ free?: boolean;
59
+ limits?: PlanLimitJson[];
60
+ featureGates?: Record<string, boolean>;
61
+ capability_limits?: Record<string, number | boolean>;
62
+ overageBehavior?: "block" | "allow_and_bill";
63
+ selfServeEnabled?: boolean;
64
+ legacy?: boolean;
65
+ archive?: PlanArchiveJson;
66
+ /** Escape hatch: A/B variants and future fields ride through here. */
67
+ [key: string]: unknown;
68
+ };
69
+ export type RouteDefinitionJson = {
70
+ match: {
71
+ method?: HttpMethod;
72
+ path: string;
73
+ };
74
+ meters?: string[] | null;
75
+ unmetered?: boolean;
76
+ action?: string;
77
+ };
78
+ export type ActionSpecJson = {
79
+ id: string;
80
+ title?: string;
81
+ kind: "query" | "mutation";
82
+ actorType?: string;
83
+ subject?: {
84
+ type: string;
85
+ from: "header" | "path_param";
86
+ name: string;
87
+ };
88
+ inputSchemaRef?: string;
89
+ audit?: "none" | "metadata" | "full";
90
+ resource?: {
91
+ resource: string;
92
+ effect: "create" | "delete";
93
+ };
94
+ };
95
+ export type RoutesFileJson = {
96
+ feature: string;
97
+ description?: string;
98
+ mutation_class?: MutationClass;
99
+ cacheProfile?: CacheProfile;
100
+ routes: RouteDefinitionJson[];
101
+ upstream?: {
102
+ override_origin: string | null;
103
+ };
104
+ policies?: string[];
105
+ runtime?: {
106
+ rollout_key?: string;
107
+ required_flags?: string[];
108
+ };
109
+ capabilities?: string[];
110
+ plans?: string[];
111
+ actions?: ActionSpecJson[];
112
+ };
113
+ export type PolicyFileJson = {
114
+ name: string;
115
+ description?: string;
116
+ type: string;
117
+ config: Record<string, unknown>;
118
+ compatible_with?: {
119
+ route_types?: string[];
120
+ meters?: string[];
121
+ auth_modes?: string[];
122
+ };
123
+ mutation_class?: MutationClass;
124
+ cacheProfile?: CacheProfile;
125
+ };
126
+ export type CapabilityFileJson = {
127
+ capability: string;
128
+ description?: string;
129
+ mutation_class?: MutationClass;
130
+ includes_features?: string[];
131
+ includes_policies?: string[];
132
+ includes_capabilities?: string[];
133
+ };
134
+ export type FrontendComponentId = "plans_table" | "usage_card" | "api_keys_panel" | "billing_summary" | "credit_balance" | "feature_panel" | "product_docs" | "audit_log" | "team_panel" | "markdown";
135
+ export type FrontendGateMode = "hide" | "disable" | "upsell";
136
+ export type FrontendNavItemJson = {
137
+ label: string;
138
+ path: string;
139
+ capability?: string;
140
+ };
141
+ export type FrontendComponentJson = {
142
+ component: FrontendComponentId;
143
+ props?: Record<string, unknown>;
144
+ capability?: string;
145
+ gateMode?: FrontendGateMode;
146
+ };
147
+ export type FrontendPageJson = {
148
+ path: string;
149
+ title: string;
150
+ requiresAuth: boolean;
151
+ capability?: string;
152
+ components?: FrontendComponentJson[];
153
+ };
154
+ export type FrontendManifestJson = {
155
+ version: 1;
156
+ nav?: FrontendNavItemJson[];
157
+ pages?: FrontendPageJson[];
158
+ };
159
+ export type MigrationEffectiveJson = "grandfather" | "next_renewal" | "by_date" | "immediate" | "opt_in";
160
+ export type MigrationProrationJson = "none" | "prorate" | "credit";
161
+ export type MigrationPlanVersionRefJson = {
162
+ plan: string;
163
+ version?: string;
164
+ };
165
+ export type MigrationTargetRefJson = {
166
+ plan: string;
167
+ version: "head";
168
+ };
169
+ export type MigrationExistingCustomersJson = {
170
+ effective: MigrationEffectiveJson;
171
+ date?: string;
172
+ proration?: MigrationProrationJson;
173
+ };
174
+ export type MigrationPinJson = {
175
+ subscriber: string;
176
+ pinTo: {
177
+ plan: string;
178
+ version: string;
179
+ };
180
+ until?: string;
181
+ notes?: string;
182
+ };
183
+ export type MigrationDeclJson = {
184
+ id: string;
185
+ from: MigrationPlanVersionRefJson;
186
+ to: MigrationTargetRefJson;
187
+ newCustomers: "immediate";
188
+ existingCustomers: MigrationExistingCustomersJson;
189
+ pins?: MigrationPinJson[];
190
+ };
191
+ export type CountedResourceScopeJson = "subscription" | "subject";
192
+ export type CountedResourceCountSourceJson = "reported" | "action_inferred";
193
+ export type CountedResourceJson = {
194
+ name: string;
195
+ display?: string;
196
+ scope?: CountedResourceScopeJson;
197
+ subjectType?: string;
198
+ countSource?: CountedResourceCountSourceJson;
199
+ };
200
+ export type ProductBlockJson = {
201
+ name: string;
202
+ displayName?: string;
203
+ description?: string;
204
+ baseUrl: string;
205
+ sandboxBaseUrl?: string;
206
+ visibility?: "public" | "private";
207
+ logoUrl?: string;
208
+ primaryColor?: string;
209
+ envBranchPrefix?: string | null;
210
+ };
211
+ export type ProductSpecJson = {
212
+ product: ProductBlockJson;
213
+ gateway?: {
214
+ authHeader?: string;
215
+ upstreamAuth?: {
216
+ type: "none" | "static_bearer";
217
+ token?: string;
218
+ };
219
+ };
220
+ metering?: {
221
+ meters?: MeterDefinitionJson[];
222
+ billOn4xx?: boolean;
223
+ };
224
+ frontend?: FrontendManifestJson;
225
+ migrations?: MigrationDeclJson[];
226
+ resources?: CountedResourceJson[];
227
+ plans?: PlanSpecJson[];
228
+ /** Legacy / advanced product blocks (usage, features, billing,
229
+ * webhooks, environments, add_ons, lifecycle, ephemeral, …) ride
230
+ * through untyped; the platform schema validates them. */
231
+ [key: string]: unknown;
232
+ };
233
+ export type ManifestIrDocument = {
234
+ irVersion: 1;
235
+ sdkVersion: string;
236
+ product: ProductSpecJson;
237
+ routes: RoutesFileJson[];
238
+ policies: PolicyFileJson[];
239
+ capabilities: CapabilityFileJson[];
240
+ runtime: {
241
+ rollout: null;
242
+ flags: null;
243
+ migrations: null;
244
+ };
245
+ };
246
+ /** `toIR()` result: the validated envelope plus its canonical hash. */
247
+ export type ManifestBuildResult = {
248
+ ir: ManifestIrDocument;
249
+ irHash: string;
250
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Price sugar — maps onto the plan's 5-knob billing fields.
3
+ *
4
+ * fs.price.monthly(29) → { recurring_fee_cents: 2900, billing_interval: "month" }
5
+ * fs.price.yearly(290) → { recurring_fee_cents: 29000, billing_interval: "year" }
6
+ * fs.price.free() → { recurring_fee_cents: 0, free: true }
7
+ */
8
+ export type PriceSpec = {
9
+ recurring_fee_cents: number;
10
+ billing_interval: "month" | "year";
11
+ free: boolean;
12
+ };
13
+ export declare const price: {
14
+ monthly(dollars: number): PriceSpec;
15
+ yearly(dollars: number): PriceSpec;
16
+ free(): PriceSpec;
17
+ };
@@ -0,0 +1,22 @@
1
+ import type { ManifestIssue } from "./errors.js";
2
+ import type { ManifestIrDocument } from "./ir-types.js";
3
+ export type ValidationResult = {
4
+ ok: true;
5
+ ir: ManifestIrDocument;
6
+ irHash: string;
7
+ } | {
8
+ ok: false;
9
+ issues: ManifestIssue[];
10
+ };
11
+ /**
12
+ * Validate a candidate envelope against the platform schemas. On success
13
+ * the returned `ir` is the JSON-normalized ORIGINAL candidate — NOT the
14
+ * zod-parsed value. The platform treats the product spec as a raw document
15
+ * (the YAML path never zod-transformed it before storing/compiling, and
16
+ * the compiler reads some fields — e.g. `plan.capabilities` — off the raw
17
+ * spec via casts that a default-stripping parse would erase). Validation
18
+ * proves the document is acceptable; the bytes stay the author's.
19
+ */
20
+ export declare function validateManifestIr(candidate: unknown): ValidationResult;
21
+ export declare function hashIr(ir: unknown): string;
22
+ export declare function canonicalIrJson(ir: unknown): string;
@@ -0,0 +1 @@
1
+ export declare const SDK_VERSION: string;
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@farthershore/product",
3
+ "version": "0.0.0",
4
+ "description": "Farther Shore product-as-code SDK — declare your business in TypeScript",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/types/index.d.ts",
8
+ "bin": {
9
+ "farthershore-manifest-build": "dist/bin.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/types/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./codegen": {
17
+ "types": "./dist/types/codegen/index.d.ts",
18
+ "import": "./dist/codegen.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=22"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "keywords": [
32
+ "farthershore",
33
+ "product-as-code",
34
+ "api",
35
+ "billing",
36
+ "api-monetization"
37
+ ],
38
+ "author": "Farther Shore",
39
+ "license": "MIT",
40
+ "dependencies": {
41
+ "tsx": "^4.20.0",
42
+ "zod": "^4.4.3"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.19.17",
46
+ "esbuild": "^0.25.0",
47
+ "eslint": "^10.3.0",
48
+ "eslint-plugin-sonarjs": "^4.0.3",
49
+ "prettier": "^3.6.2",
50
+ "typescript": "^6.0.2",
51
+ "typescript-eslint": "^8.59.0",
52
+ "vitest": "^4.1.6",
53
+ "@farthershore/contracts": "0.61.1"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc --noEmit && node scripts/build.mjs",
57
+ "pack:check": "node scripts/check-pack.mjs",
58
+ "test": "vitest run",
59
+ "lint": "prettier --check \"src/**/*.ts\" && eslint .",
60
+ "lint:fix": "eslint . --fix",
61
+ "typecheck": "tsc --noEmit"
62
+ }
63
+ }