@farthershore/product 0.0.0 → 0.2.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.
@@ -1,5 +1,6 @@
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";
1
+ import type { CacheProfile, ActionSpecJson, CapabilityFileJson, FrontendComponentId, FrontendGateMode, FrontendManifestJson, GrantJson, ManifestBuildResult, MeterDefinitionJson, MigrationDeclJson, MigrationPinJson, MutationClass, CountedResourceJson, PlanLimitJson, PlanMeterJson, PlanSpecJson, PolicyFileJson, ProductPoliciesJson, ProductCustomerContextJson, RoutesFileJson } from "./ir-types.js";
2
2
  import type { PriceSpec } from "./price.js";
3
+ import { type ManifestResourceGraphSnapshot } from "./resource-graph.js";
3
4
  /** Brand symbol so the bin can recognize a Business across SDK copies
4
5
  * (the repo's pinned SDK vs whatever loaded the entry). */
5
6
  export declare const BUSINESS_BRAND: unique symbol;
@@ -20,6 +21,14 @@ export type BusinessOptions = {
20
21
  token?: string;
21
22
  };
22
23
  billOn4xx?: boolean;
24
+ /** Product operator policies, e.g. zero-traffic cleanup report/PR mode. */
25
+ operatorPolicies?: ProductPoliciesJson;
26
+ /** Customer-context controls emitted as product.customer_context. */
27
+ customerContext?: {
28
+ identityRequirement?: ProductCustomerContextJson["identity_requirement"];
29
+ contextTokens?: ProductCustomerContextJson["context_tokens"];
30
+ portalAuth?: ProductCustomerContextJson["portal_auth"];
31
+ };
23
32
  billing?: {
24
33
  gracePeriodDays?: number;
25
34
  applyLimitUpgradesInstantly?: boolean;
@@ -182,17 +191,16 @@ export type PlanCapabilityGrant = {
182
191
  readonly capability: string;
183
192
  readonly limits?: Record<string, number | boolean>;
184
193
  };
194
+ /**
195
+ * A synchronous Product SDK module. Use this to split a product across files:
196
+ * `product.use(configureMeters, configureRoutes, configurePlans)`.
197
+ */
198
+ export type ProductModule = (product: Business) => void;
185
199
  export declare class Business {
186
200
  readonly [BUSINESS_BRAND] = true;
187
201
  readonly name: string;
188
202
  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;
203
+ private readonly graph;
196
204
  private frontendManifest?;
197
205
  private productPatch;
198
206
  /** Sugar for binding API routes to features. */
@@ -228,6 +236,9 @@ export declare class Business {
228
236
  feature(key: string, options?: FeatureOptions): FeatureRef;
229
237
  policy(name: string, options: PolicyOptions): PolicyRef;
230
238
  plan(key: string, options: PlanOptions): PlanRef;
239
+ use(...modules: ProductModule[]): Business;
240
+ /** Inspect the SDK-local declaration graph used to emit the Manifest IR. */
241
+ resourceGraph(): ManifestResourceGraphSnapshot;
231
242
  /** Assemble + validate the Manifest IR. Throws ManifestValidationError
232
243
  * with structured issues when the declared state is invalid. */
233
244
  toIR(): ManifestBuildResult;
@@ -236,9 +247,23 @@ export declare class Business {
236
247
  private ensureFrontendManifest;
237
248
  private normalizeMigrationPlanRef;
238
249
  private normalizeMigrationTargetRef;
239
- private assertUniqueMigrationId;
240
250
  private assertUniqueMigrationIds;
241
251
  private assertNewKey;
252
+ private assertGraphDependenciesSatisfied;
253
+ private getFeatureFile;
254
+ private registerFeatureFile;
255
+ private syncFeatureGraphNode;
256
+ private registerAction;
257
+ private syncFrontendGraphNode;
258
+ private capabilityDependsOn;
259
+ private policyDependsOn;
260
+ private featureDependsOn;
261
+ private actionDependsOn;
262
+ private planDependsOn;
263
+ private migrationDependsOn;
264
+ private frontendDependsOn;
265
+ private dependenciesFor;
266
+ private existingDependenciesFor;
242
267
  }
243
268
  export declare function isBusiness(value: unknown): value is Business;
244
- export declare function business(name: string, options: BusinessOptions): Business;
269
+ export declare function business(name: string, options: BusinessOptions, configure?: ProductModule): Business;
@@ -14,7 +14,8 @@ export { price } from "./price.js";
14
14
  export { validateManifestIr, hashIr, canonicalIrJson } from "./validate.js";
15
15
  export { ManifestValidationError, ManifestBuilderError } from "./errors.js";
16
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";
17
+ export type { BusinessOptions, MeterOptions, ResourceOptions, CapabilityOptions, ActionOptions, FrontendNavItemOptions, FrontendPageOptions, FrontendComponentOptions, MigrationOptions, FeatureOptions, RouteOptions, PolicyOptions, PlanOptions, MeterRef, ResourceRef, ActionRef, PolicyRef, PlanRef, FeatureRef, CapabilityRef, PlanCapabilityGrant, ProductModule, } from "./business.js";
18
+ export type { ManifestResourceGraphSnapshot, ManifestResourceKind, ManifestResourceUrn, } from "./resource-graph.js";
18
19
  export type { PriceSpec } from "./price.js";
19
20
  export type { ManifestIssue } from "./errors.js";
20
21
  export type { ValidationResult } from "./validate.js";
@@ -197,6 +197,29 @@ export type CountedResourceJson = {
197
197
  subjectType?: string;
198
198
  countSource?: CountedResourceCountSourceJson;
199
199
  };
200
+ export type ProductCleanupPolicyModeJson = "report" | "pull_request";
201
+ export type ProductChangeApprovalRiskJson = "safe" | "non_blocking" | "economic_risk" | "blocking";
202
+ export type ProductPoliciesJson = {
203
+ cleanup?: {
204
+ enabled?: boolean;
205
+ mode?: ProductCleanupPolicyModeJson;
206
+ };
207
+ change_approval?: {
208
+ auto_merge_max_risk?: "none" | "safe" | "non_blocking";
209
+ require_human_for?: ProductChangeApprovalRiskJson[];
210
+ };
211
+ };
212
+ export type CustomerIdentityRequirementJson = "org_only" | "org_and_user";
213
+ export type CustomerPortalAuthStrategyJson = "clerk" | "test-personas";
214
+ export type ProductCustomerContextJson = {
215
+ identity_requirement?: CustomerIdentityRequirementJson;
216
+ context_tokens?: {
217
+ enabled?: boolean;
218
+ };
219
+ portal_auth?: {
220
+ strategy: CustomerPortalAuthStrategyJson;
221
+ };
222
+ };
200
223
  export type ProductBlockJson = {
201
224
  name: string;
202
225
  displayName?: string;
@@ -224,6 +247,8 @@ export type ProductSpecJson = {
224
247
  frontend?: FrontendManifestJson;
225
248
  migrations?: MigrationDeclJson[];
226
249
  resources?: CountedResourceJson[];
250
+ policies?: ProductPoliciesJson;
251
+ customer_context?: ProductCustomerContextJson;
227
252
  plans?: PlanSpecJson[];
228
253
  /** Legacy / advanced product blocks (usage, features, billing,
229
254
  * webhooks, environments, add_ons, lifecycle, ephemeral, …) ride
@@ -0,0 +1,36 @@
1
+ export type ManifestResourceKind = "product" | "meter" | "counted_resource" | "capability" | "policy" | "feature" | "action" | "plan" | "frontend" | "lifecycle_migration";
2
+ export type ManifestResourceUrn = `urn:farthershore:product:${ManifestResourceKind}:${string}`;
3
+ export type ManifestResourceRef = {
4
+ readonly kind: ManifestResourceKind;
5
+ readonly key: string;
6
+ };
7
+ export type ManifestResourceNode<T = unknown> = {
8
+ readonly urn: ManifestResourceUrn;
9
+ readonly kind: ManifestResourceKind;
10
+ readonly key: string;
11
+ readonly value: T;
12
+ readonly dependsOn: readonly ManifestResourceUrn[];
13
+ readonly declarationOrder: number;
14
+ };
15
+ export type ManifestResourceGraphSnapshot = {
16
+ readonly nodes: readonly Omit<ManifestResourceNode, "value">[];
17
+ };
18
+ export type MissingManifestResourceDependency = {
19
+ readonly from: ManifestResourceUrn;
20
+ readonly missing: ManifestResourceUrn;
21
+ };
22
+ export declare class ManifestResourceGraph {
23
+ private readonly nodes;
24
+ private declarationOrder;
25
+ register<T>(kind: ManifestResourceKind, key: string, value: T, dependsOn?: readonly ManifestResourceUrn[]): ManifestResourceNode<T>;
26
+ upsert<T>(kind: ManifestResourceKind, key: string, value: T, dependsOn?: readonly ManifestResourceUrn[]): ManifestResourceNode<T>;
27
+ clearKind(kind: ManifestResourceKind): void;
28
+ has(kind: ManifestResourceKind, key: string): boolean;
29
+ get<T>(kind: ManifestResourceKind, key: string): ManifestResourceNode<T> | null;
30
+ values<T>(kind: ManifestResourceKind): T[];
31
+ sortedValues<T>(kind: ManifestResourceKind, key: (value: T) => string): T[];
32
+ snapshot(): ManifestResourceGraphSnapshot;
33
+ missingDependencies(): MissingManifestResourceDependency[];
34
+ }
35
+ export declare function resourceUrn(kind: ManifestResourceKind, key: string): ManifestResourceUrn;
36
+ export declare function resourceDependency(kind: ManifestResourceKind, key: string): ManifestResourceUrn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farthershore/product",
3
- "version": "0.0.0",
3
+ "version": "0.2.0",
4
4
  "description": "Farther Shore product-as-code SDK — declare your business in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",