@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,303 @@
1
+ import { createRequire as __createRequire } from "node:module";const require=__createRequire(import.meta.url);
2
+
3
+ // src/codegen/index.ts
4
+ var PRODUCT_BLOCK_KEYS = /* @__PURE__ */ new Set([
5
+ "name",
6
+ "displayName",
7
+ "description",
8
+ "sandboxBaseUrl",
9
+ "visibility",
10
+ "logoUrl",
11
+ "primaryColor",
12
+ "envBranchPrefix",
13
+ "baseUrl"
14
+ ]);
15
+ var GATEWAY_KEYS = /* @__PURE__ */ new Set(["authHeader", "upstreamAuth"]);
16
+ var METERING_KEYS = /* @__PURE__ */ new Set(["meters", "billOn4xx"]);
17
+ var BILLING_KEYS = /* @__PURE__ */ new Set([
18
+ "gracePeriodDays",
19
+ "applyLimitUpgradesInstantly"
20
+ ]);
21
+ var TOP_LEVEL_HANDLED = /* @__PURE__ */ new Set([
22
+ "product",
23
+ "gateway",
24
+ "metering",
25
+ "billing",
26
+ "frontend",
27
+ "migrations",
28
+ "plans"
29
+ ]);
30
+ var PLAN_HANDLED_KEYS = /* @__PURE__ */ new Set([
31
+ "key",
32
+ "name",
33
+ "description",
34
+ "details",
35
+ "recurring_fee_cents",
36
+ "billing_interval",
37
+ "free",
38
+ "meters",
39
+ "grants",
40
+ "trial_days",
41
+ "max_monthly_spend_cents",
42
+ "min_monthly_spend_cents",
43
+ "limits",
44
+ "featureGates",
45
+ "capability_limits",
46
+ "capabilities",
47
+ "overageBehavior",
48
+ "selfServeEnabled",
49
+ "legacy",
50
+ "archive"
51
+ ]);
52
+ function generateManifestSource(ir) {
53
+ const lines = [];
54
+ const product = ir.product;
55
+ lines.push(`import { fs } from "@farthershore/product";`);
56
+ lines.push("");
57
+ lines.push(
58
+ `const business = fs.business(${lit(product.product.name)}, ${lit(
59
+ businessOptions(product),
60
+ 0
61
+ )});`
62
+ );
63
+ const meters = product.metering?.meters ?? [];
64
+ if (meters.length) {
65
+ lines.push("");
66
+ for (const meter of meters) {
67
+ const { key, ...rest } = meter;
68
+ lines.push(`business.meter(${lit(key)}, ${lit(rest, 0)});`);
69
+ }
70
+ }
71
+ if (ir.capabilities.length) {
72
+ lines.push("");
73
+ for (const cap of ir.capabilities) {
74
+ const options = {};
75
+ if (cap.description !== void 0) options.description = cap.description;
76
+ if (cap.mutation_class !== void 0)
77
+ options.mutationClass = cap.mutation_class;
78
+ if (cap.includes_features?.length)
79
+ options.includesFeatures = cap.includes_features;
80
+ if (cap.includes_policies?.length)
81
+ options.includesPolicies = cap.includes_policies;
82
+ if (cap.includes_capabilities?.length)
83
+ options.includesCapabilities = cap.includes_capabilities;
84
+ lines.push(
85
+ `business.capability(${lit(cap.capability)}, ${lit(options, 0)});`
86
+ );
87
+ }
88
+ }
89
+ if (ir.policies.length) {
90
+ lines.push("");
91
+ for (const policy of ir.policies) {
92
+ const options = {
93
+ type: policy.type,
94
+ config: policy.config
95
+ };
96
+ if (policy.description !== void 0)
97
+ options.description = policy.description;
98
+ if (policy.mutation_class !== void 0)
99
+ options.mutationClass = policy.mutation_class;
100
+ if (policy.cacheProfile !== void 0)
101
+ options.cacheProfile = policy.cacheProfile;
102
+ if (policy.compatible_with !== void 0) {
103
+ const cw = policy.compatible_with;
104
+ options.compatibleWith = {
105
+ ...cw.route_types ? { routeTypes: cw.route_types } : {},
106
+ ...cw.meters ? { meters: cw.meters } : {},
107
+ ...cw.auth_modes ? { authModes: cw.auth_modes } : {}
108
+ };
109
+ }
110
+ lines.push(`business.policy(${lit(policy.name)}, ${lit(options, 0)});`);
111
+ }
112
+ }
113
+ if (ir.routes.length) {
114
+ lines.push("");
115
+ for (const file of ir.routes) {
116
+ const options = {};
117
+ if (file.description !== void 0)
118
+ options.description = file.description;
119
+ if (file.mutation_class !== void 0)
120
+ options.mutationClass = file.mutation_class;
121
+ if (file.cacheProfile !== void 0)
122
+ options.cacheProfile = file.cacheProfile;
123
+ if (file.upstream !== void 0)
124
+ options.upstreamOrigin = file.upstream.override_origin;
125
+ if (file.policies?.length) options.policies = file.policies;
126
+ if (file.capabilities?.length) options.capabilities = file.capabilities;
127
+ if (file.plans?.length) options.plans = file.plans;
128
+ if (file.actions?.length) options.actions = file.actions;
129
+ if (file.runtime?.rollout_key !== void 0)
130
+ options.rolloutKey = file.runtime.rollout_key;
131
+ if (file.runtime?.required_flags?.length)
132
+ options.requiredFlags = file.runtime.required_flags;
133
+ options.routes = file.routes.map((route) => ({
134
+ match: `${route.match.method ?? "*"} ${route.match.path}`,
135
+ ...route.meters && route.meters.length ? { meters: route.meters } : {},
136
+ ...route.unmetered !== void 0 ? { unmetered: route.unmetered } : {},
137
+ ...route.action !== void 0 ? { action: route.action } : {}
138
+ }));
139
+ lines.push(`business.feature(${lit(file.feature)}, ${lit(options, 0)});`);
140
+ }
141
+ }
142
+ if (product.frontend !== void 0) {
143
+ lines.push("");
144
+ lines.push(`business.frontend.manifest(${lit(product.frontend, 0)});`);
145
+ }
146
+ if (product.migrations?.length) {
147
+ lines.push("");
148
+ lines.push(`business.lifecycle.migrations(${lit(product.migrations, 0)});`);
149
+ }
150
+ const plans = product.plans ?? [];
151
+ if (plans.length) {
152
+ lines.push("");
153
+ for (const plan of plans) {
154
+ lines.push(
155
+ `business.plan(${lit(plan.key)}, ${lit(planOptions(plan), 0)});`
156
+ );
157
+ }
158
+ }
159
+ const patch = productPatch(product);
160
+ if (Object.keys(patch).length) {
161
+ lines.push("");
162
+ lines.push(`business.raw.productPatch(${lit(patch, 0)});`);
163
+ }
164
+ lines.push("");
165
+ lines.push("export default business;");
166
+ lines.push("");
167
+ return lines.join("\n");
168
+ }
169
+ function businessOptions(product) {
170
+ const block = product.product;
171
+ const options = { baseUrl: block.baseUrl };
172
+ if (block.displayName !== void 0) options.displayName = block.displayName;
173
+ if (block.description !== void 0) options.description = block.description;
174
+ if (block.sandboxBaseUrl !== void 0)
175
+ options.sandboxBaseUrl = block.sandboxBaseUrl;
176
+ if (block.visibility !== void 0) options.visibility = block.visibility;
177
+ if (block.logoUrl !== void 0) options.logoUrl = block.logoUrl;
178
+ if (block.primaryColor !== void 0)
179
+ options.primaryColor = block.primaryColor;
180
+ if (block.envBranchPrefix !== void 0)
181
+ options.envBranchPrefix = block.envBranchPrefix;
182
+ const gateway = product.gateway ?? {};
183
+ if (gateway.authHeader !== void 0) options.authHeader = gateway.authHeader;
184
+ if (gateway.upstreamAuth !== void 0)
185
+ options.upstreamAuth = gateway.upstreamAuth;
186
+ if (product.metering?.billOn4xx !== void 0)
187
+ options.billOn4xx = product.metering.billOn4xx;
188
+ const billing = product.billing;
189
+ if (billing) {
190
+ const handled = {};
191
+ if (billing.gracePeriodDays !== void 0)
192
+ handled.gracePeriodDays = billing.gracePeriodDays;
193
+ if (billing.applyLimitUpgradesInstantly !== void 0)
194
+ handled.applyLimitUpgradesInstantly = billing.applyLimitUpgradesInstantly;
195
+ if (Object.keys(handled).length) options.billing = handled;
196
+ }
197
+ return options;
198
+ }
199
+ function planOptions(plan) {
200
+ const options = { name: plan.name };
201
+ if (plan.description !== void 0) options.description = plan.description;
202
+ if (plan.details !== void 0) options.details = plan.details;
203
+ const raw = {};
204
+ const fee = plan.recurring_fee_cents;
205
+ const interval = plan.billing_interval;
206
+ const free = plan.free;
207
+ if (fee !== void 0 && interval !== void 0 && free !== true && fee % 1 === 0) {
208
+ options.price = {
209
+ __price: interval === "year" ? "yearly" : "monthly",
210
+ amount: fee / 100
211
+ };
212
+ if (free !== void 0) raw.free = free;
213
+ } else if (free === true && fee === 0 && interval === "month") {
214
+ options.price = { __price: "free" };
215
+ } else {
216
+ if (fee !== void 0) raw.recurring_fee_cents = fee;
217
+ if (interval !== void 0) raw.billing_interval = interval;
218
+ if (free !== void 0) raw.free = free;
219
+ }
220
+ if (plan.meters !== void 0) options.meters = plan.meters;
221
+ if (plan.grants !== void 0) options.grants = plan.grants;
222
+ if (plan.trial_days !== void 0) options.trialDays = plan.trial_days;
223
+ if (plan.max_monthly_spend_cents !== void 0)
224
+ options.maxMonthlySpendCents = plan.max_monthly_spend_cents;
225
+ if (plan.min_monthly_spend_cents !== void 0)
226
+ options.minMonthlySpendCents = plan.min_monthly_spend_cents;
227
+ if (plan.limits !== void 0) options.limits = plan.limits;
228
+ if (plan.featureGates !== void 0) options.featureGates = plan.featureGates;
229
+ if (plan.capability_limits !== void 0)
230
+ options.capabilityLimits = plan.capability_limits;
231
+ if (plan.capabilities !== void 0) options.capabilities = plan.capabilities;
232
+ if (plan.overageBehavior !== void 0)
233
+ options.overageBehavior = plan.overageBehavior;
234
+ if (plan.selfServeEnabled !== void 0)
235
+ options.selfServeEnabled = plan.selfServeEnabled;
236
+ if (plan.legacy !== void 0) options.legacy = plan.legacy;
237
+ if (plan.archive !== void 0) options.archive = plan.archive;
238
+ for (const [key, value] of Object.entries(plan)) {
239
+ if (!PLAN_HANDLED_KEYS.has(key)) raw[key] = value;
240
+ }
241
+ if (Object.keys(raw).length) options.raw = raw;
242
+ return options;
243
+ }
244
+ function productPatch(product) {
245
+ const patch = {};
246
+ for (const [key, value] of Object.entries(product)) {
247
+ if (!TOP_LEVEL_HANDLED.has(key)) patch[key] = value;
248
+ }
249
+ const blockLeftovers = (block, handled) => {
250
+ if (typeof block !== "object" || block === null) return {};
251
+ const out = {};
252
+ for (const [key, value] of Object.entries(block)) {
253
+ if (!handled.has(key)) out[key] = value;
254
+ }
255
+ return out;
256
+ };
257
+ const productLeft = blockLeftovers(product.product, PRODUCT_BLOCK_KEYS);
258
+ if (Object.keys(productLeft).length) patch.product = productLeft;
259
+ const gatewayLeft = blockLeftovers(product.gateway, GATEWAY_KEYS);
260
+ if (Object.keys(gatewayLeft).length) patch.gateway = gatewayLeft;
261
+ const meteringLeft = blockLeftovers(product.metering, METERING_KEYS);
262
+ if (Object.keys(meteringLeft).length) patch.metering = meteringLeft;
263
+ const billingLeft = blockLeftovers(product.billing, BILLING_KEYS);
264
+ if (Object.keys(billingLeft).length) patch.billing = billingLeft;
265
+ return patch;
266
+ }
267
+ function lit(value, indent = 0) {
268
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && "__price" in value) {
269
+ const marker = value;
270
+ if (marker.__price === "free") return "fs.price.free()";
271
+ return `fs.price.${marker.__price}(${marker.amount})`;
272
+ }
273
+ if (Array.isArray(value)) {
274
+ if (value.length === 0) return "[]";
275
+ const inner = value.map((item) => lit(item, indent + 1));
276
+ const oneLine = `[${inner.join(", ")}]`;
277
+ if (oneLine.length <= 72 && !oneLine.includes("\n")) return oneLine;
278
+ const pad = " ".repeat(indent + 1);
279
+ return `[
280
+ ${inner.map((item) => `${pad}${item}`).join(",\n")},
281
+ ${" ".repeat(indent)}]`;
282
+ }
283
+ if (typeof value === "object" && value !== null) {
284
+ const entries = Object.entries(value);
285
+ if (entries.length === 0) return "{}";
286
+ const inner = entries.map(
287
+ ([key, val]) => `${identifier(key)}: ${lit(val, indent + 1)}`
288
+ );
289
+ const oneLine = `{ ${inner.join(", ")} }`;
290
+ if (oneLine.length <= 72 && !oneLine.includes("\n")) return oneLine;
291
+ const pad = " ".repeat(indent + 1);
292
+ return `{
293
+ ${inner.map((entry) => `${pad}${entry}`).join(",\n")},
294
+ ${" ".repeat(indent)}}`;
295
+ }
296
+ return JSON.stringify(value);
297
+ }
298
+ function identifier(key) {
299
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
300
+ }
301
+ export {
302
+ generateManifestSource
303
+ };