UNPKG

@swell/cli

Version:

Swell's command line interface/utility

268 lines (267 loc) 10.6 kB
import { GroupInfo } from './table.js'; /** * Manifest entry shape, matching `extensions[]` in the deployed app record * and in local `./swell.json`. Mirrors the schema at * schema-api-server/api/admin/models/apps.json:429-491. */ export interface ManifestEntry { id: string; type: 'payment' | 'tax' | 'shipping'; name?: string; description?: string; setting?: string; subscriptions?: boolean; method?: string; gateway?: string; method_logo_src?: string; method_icon_src?: string; gateway_logo_src?: string; gateway_icon_src?: string; carrier?: string; carrier_logo_src?: string; carrier_icon_src?: string; [key: string]: unknown; } /** CLI-resolved type. Diverges from raw manifest `type` for payment (card vs alt). */ export type ExtensionType = 'card' | 'alt' | 'shipping' | 'tax'; export type ExtensionStatus = 'not deployed' | 'not activated' | 'app id mismatch' | 'id mismatch' | 'not selected' | 'gateway missing' | 'not enabled' | 'no handler' | 'handler mismatch' | 'activated'; export type ActionOwner = 'dev' | 'merchant' | null; export interface FunctionRecord { id?: string; name?: string; enabled?: boolean; app_id?: string; extension?: string; model?: { events?: string[]; }; } export interface ComponentRecord { id?: string; name?: string; file_path?: string; values?: { [key: string]: unknown; extension?: string; }; [key: string]: unknown; } export interface PaymentMethodRecord { id?: string; name?: string; gateway?: string; extension_app_id?: string; extension_config_id?: string; enabled?: boolean; activated?: boolean; [key: string]: unknown; } export interface PaymentGatewayRecord { id?: string; name?: string; extension_app_id?: string; extension_config_id?: string; [key: string]: unknown; } export interface ShippingCarrierRecord { id?: string; name?: string; enabled?: boolean; extension_app_id?: string; extension_config_id?: string; [key: string]: unknown; } export interface TaxSettingsRecord { extension_app_id?: string; extension_config_id?: string; [key: string]: unknown; } export interface PaymentSettings { methods?: PaymentMethodRecord[]; gateways?: PaymentGatewayRecord[]; [key: string]: unknown; } export interface ShippingSettings { carriers?: ShippingCarrierRecord[]; [key: string]: unknown; } export interface NativeBinding { path: string; record: unknown; field_checks: Record<string, boolean>; } export interface BoundFunction { id?: string; name?: string; extension?: string; enabled?: boolean; model?: { events?: string[]; }; } export interface BoundComponent { id?: string; name?: string; extension?: string; file_path?: string; } export interface LocalDiff { changed_fields: string[]; local: ManifestEntry; deployed: ManifestEntry; } export interface ExtensionDetail { status: ExtensionStatus; action_owner: ActionOwner; action: string | null; type: ExtensionType | null; manifest: ManifestEntry | null; native_bindings: NativeBinding[]; bound: { components: BoundComponent[]; functions: BoundFunction[]; }; required_events: string[]; missing_required_events: string[]; local_diff: LocalDiff | null; } /** Parsed identifier for `swell inspect extensions <id>`. */ export type ParsedExtensionKey = { appPart: string; extId: string; kind: 'slug'; } | { kind: 'name'; name: string; } | { input: string; kind: 'invalid'; }; /** * Parse a `swell inspect extensions` identifier. * * Accepts: * - `app.<slug>.<extId>` → kind 'slug' * - bare `<extId>` → kind 'name' (requires --app= scope at the call site) * * 24-char hex is intentionally NOT a valid form — the synthesized resource has * no canonical 24-char id. Callers should reject hex shapes upstream so the * error message can be clear about the cause. */ export declare function parseExtensionKey(input: string): ParsedExtensionKey; /** Build the column-1 paste-back key for a row. */ export declare function formatExtensionKey(slug: string, extId: string): string; /** * Resolve a manifest entry to the CLI's type taxonomy. Payment splits on * `method`: when `method === 'card'` the extension provides a card gateway; * otherwise it's an alt method. The platform formula at * schema-api-server/api/admin/models/apps.json:457-458 defaults `method` to * `id` when unset, so a payment extension whose `id` happens to be `card` * resolves to `card` even with no explicit `method`. */ export declare function resolveExtensionType(entry: Pick<ManifestEntry, 'type' | 'method' | 'id'>): ExtensionType; /** Apply the platform's `if(method, method, id)` formula. */ export declare function paymentMethodIdFor(entry: Pick<ManifestEntry, 'method' | 'id'>): string; /** Shipping records use `app_<appId>_<extId>` as both the row id and the carrier id. */ export declare function shippingBindId(appId: string, extId: string): string; /** Card-gateway records use the same id form as shipping rows. */ export declare function gatewayBindId(appId: string, extId: string): string; /** * Required events per extension type, in the `<model>/<event>` form that * function records actually store. * * Verified against: * schema-api-server/server/vault.js:1818,1894 (payment.create_intent) * schema-api-server/api/com/features/payments/index.js:701-757 * (payment.charge, payment.refund, dispatcher gate) * schema-api-server/server/vault.js:2304-2322 (card-gateway intent path) * schema-api-server/api/com/features/orders/shipping.js:411-417,424-431,452-458 * (shipping dispatch + enabled gate) * schema-api-server/api/com/features/orders/taxes.js:123-176 * (tax dispatch) * schema-api-server/api/com/features/orders/extensions.test.js:85,95,179,271,290,303,671,704 * (event-name format fixtures) */ export declare function requiredEventsFor(type: ExtensionType): string[]; /** * Strip `before:` / `after:` hook-type prefix from an event identifier. * * Both prefixes are platform-supported on function records; bare events * default to `after`. For required-event coverage we treat any prefix as * equivalent so that a function declaring `before:payment.charge` covers a * required `payments/payment.charge`. Cite: * schema-api-server/api/com/features/orders/extensions.test.js:95. */ export declare function stripHookPrefix(event: string): string; /** * Compute the set of bare event identifiers covered by a function record's * `model.events` list. The function event format is `<model>/<event>` with * an optional `before:`/`after:` prefix on the bare event part. Returns the * full `<model>/<event>` string with hook prefixes stripped from the event. */ export declare function eventsCoveredByFunctions(functions: Pick<FunctionRecord, 'model'>[]): Set<string>; /** Required events not covered by any of the bound functions. */ export declare function missingRequiredEvents(type: ExtensionType, functions: Pick<FunctionRecord, 'model'>[]): string[]; /** Lift a component record into the synthesized `BoundComponent` shape. */ export declare function liftBoundComponent(record: ComponentRecord): BoundComponent; /** Lift a function record into the synthesized `BoundFunction` shape. */ export declare function liftBoundFunction(record: FunctionRecord): BoundFunction; /** * Compare a local manifest entry against the deployed entry. Uses deep * equality for change detection and reports differing top-level keys. When * the entries are equal, returns `null`. */ export declare function diffManifestEntries(local: ManifestEntry, deployed: ManifestEntry): LocalDiff | null; export interface BuildDetailInput { appId: string; appSlug: string; extId: string; manifest: ManifestEntry | null; /** True when the local manifest has the entry but the deployed app record doesn't. */ notDeployed?: boolean; payments?: PaymentSettings | null; shipments?: ShippingSettings | null; taxes?: TaxSettingsRecord | null; /** Functions in the same app whose top-level `extension` matches this extId. */ functions: FunctionRecord[]; /** Components in the same app whose `values.extension` matches this extId. */ components: ComponentRecord[]; localDiff?: LocalDiff | null; } export interface BuildOrphanInput { appId: string; appSlug: string; /** The unresolved extension value taken from the function/component. */ unresolvedId: string; functions: FunctionRecord[]; components: ComponentRecord[]; } /** Build the synthesized envelope for a regular (non-orphan) extension row. */ export declare function buildExtensionDetail(input: BuildDetailInput): ExtensionDetail; /** Build the synthesized envelope for an orphan row. */ export declare function buildOrphanDetail(input: BuildOrphanInput): ExtensionDetail; /** * List-mode meta string for one row. Returns `undefined` when the row has * nothing surface-worthy beyond the type tag (which is always present). */ export declare function listMetaFor(detail: ExtensionDetail): string; /** * List-mode meta string for an orphan row. The type tag is replaced with * the status word since there's no manifest to derive type from. */ export declare function orphanListMeta(fnCount: number, compCount: number): string; /** * `Next steps:` lines per status. Runnable shell commands come first, * merchant-UI lines come second prefixed with `(merchant)` so an agent can * filter. Empty array when there are no actionable next steps. */ export declare function nextStepLines(detail: ExtensionDetail): string[]; /** * Section ordering for the list view. Apps render alphabetically; orphan * rows land in a single trailing `<orphans>` group. */ export declare function listGroupForApp(slug: string): GroupInfo; export declare function listGroupForOrphans(): GroupInfo; /** True when a function/component "belongs to" an extension by direct match. */ export declare function functionMatchesExtension(fn: FunctionRecord, extId: string): boolean; export declare function componentMatchesExtension(comp: ComponentRecord, extId: string): boolean;