@farthershore/product 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # @farthershore/product
2
+
3
+ Product-as-Code SDK for Farther Shore. Builder repos use this package in
4
+ `product.config.ts` to declare product contracts in TypeScript. Builders author
5
+ and export a `Business`; the Farther Shore GitHub bot checks and publishes those
6
+ declarations when repo changes land.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @farthershore/product
12
+ ```
13
+
14
+ Builder repos generated by Farther Shore already pin this dependency in
15
+ `package.json`.
16
+
17
+ ## Example
18
+
19
+ ```ts
20
+ import { fs } from "@farthershore/product";
21
+
22
+ const business = fs.business("croncloud", {
23
+ baseUrl: "https://api.example.com",
24
+ displayName: "CronCloud",
25
+ description: "Managed cron jobs",
26
+ });
27
+
28
+ business.meter("requests", { display: "Requests" });
29
+
30
+ business.resource("cron_jobs", {
31
+ display: "Cron jobs",
32
+ countSource: "action_inferred",
33
+ });
34
+
35
+ const cron = business.capability("managed-cron", {
36
+ title: "Managed Cron Jobs",
37
+ });
38
+
39
+ const jobs = business.feature("cron-jobs", {
40
+ description: "Cron job CRUD",
41
+ capabilities: [cron],
42
+ routes: [{ match: "GET /v1/cron-jobs", meters: ["requests"] }],
43
+ });
44
+
45
+ const createJob = jobs.action("cron-job.create", {
46
+ kind: "mutation",
47
+ title: "Create cron job",
48
+ resource: { resource: "cron_jobs", effect: "create" },
49
+ });
50
+
51
+ jobs.route("POST /v1/cron-jobs", {
52
+ meters: ["requests"],
53
+ action: createJob,
54
+ });
55
+
56
+ business.plan("starter", {
57
+ name: "Starter",
58
+ price: fs.price.monthly(29),
59
+ grants: [cron.enable({ limits: { cron_jobs: 10 } })],
60
+ limits: [
61
+ {
62
+ dimension: "requests",
63
+ window: { type: "named", name: "minute" },
64
+ capacity: 600,
65
+ enforcement: "enforce",
66
+ },
67
+ ],
68
+ });
69
+
70
+ export default business;
71
+ ```
72
+
73
+ ## Bot build
74
+
75
+ Normal product repos do not call platform release or IR APIs. The GitHub bot
76
+ owns that workflow:
77
+
78
+ 1. Loads `product.config.ts`.
79
+ 2. Requires the default export to be the `Business` returned by
80
+ `fs.business(...)` or `fs.product(...)`.
81
+ 3. Compiles the business into deterministic Manifest IR.
82
+ 4. Validates the result against the deployed platform contract.
83
+ 5. Publishes the accepted release through Core so edge artifacts propagate.
84
+
85
+ The bundled `farthershore-manifest-build` binary is bot/build-runner plumbing.
86
+ Run it locally only when debugging the automation itself; it is not part of the
87
+ normal builder workflow.
88
+
89
+ ## Public API
90
+
91
+ - `fs.business(name, options)` / `fs.product(name, options)` — create the
92
+ product builder.
93
+ - `business.meter(...)` — declare billable or enforceable dimensions.
94
+ - `business.resource(...)` — declare counted resources for resource-count
95
+ constraints.
96
+ - `business.capability(...)` — declare capability bundles and plan grants.
97
+ - `business.feature(...)` / `business.api.route(...)` — declare gateway routes,
98
+ meters, and action metadata.
99
+ - `business.policy(...)` — declare policy files in code.
100
+ - `business.plan(...)` — declare plan pricing, limits, grants, and lifecycle
101
+ behavior.
102
+ - `business.frontend.*` — declare generated portal navigation and gates.
103
+ - `business.lifecycle.*` — declare migrations.
104
+ - `business.raw.*` — escape hatches for platform-schema JSON when the typed SDK
105
+ does not yet have sugar.
106
+
107
+ ## Determinism
108
+
109
+ `product.config.ts` must be deterministic: no dates, randomness, network calls,
110
+ or process state. Sorted collections produce stable output, while route order
111
+ remains semantic because the gateway matcher is first-match-wins.
112
+
113
+ The GitHub bot runs the build twice and rejects the push if the two generated
114
+ hashes differ.
115
+
116
+ ## Release
117
+
118
+ `@farthershore/product` publishes to public npm through the GitHub workflow
119
+ `.github/workflows/publish-product-sdk.yml`.
120
+
121
+ Release sequence:
122
+
123
+ 1. Bump `packages/product/package.json` version.
124
+ 2. Run the package checks:
125
+ ```bash
126
+ pnpm --filter @farthershore/product run lint
127
+ pnpm --filter @farthershore/product run test
128
+ pnpm --filter @farthershore/product run build
129
+ pnpm --filter @farthershore/product run pack:check
130
+ ```
131
+ 3. Commit, push, and merge through the normal PR flow.
132
+ 4. Tag from `main` with the exact package version:
133
+ ```bash
134
+ git tag product-v<version>
135
+ git push origin product-v<version>
136
+ ```
137
+
138
+ The publish workflow verifies that the tag equals
139
+ `product-v<packages/product/package.json version>`, builds/tests the package and
140
+ its dependencies, runs `pack:check`, then publishes with `NPM_PUBLISH_TOKEN`.