@pwfabric/authoring 0.0.0-stage → 1.0.0-rc.1
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/LICENSE +202 -0
- package/README.md +50 -2
- package/dist/define-block.d.ts +111 -0
- package/dist/define-block.js +297 -0
- package/dist/define-block.js.map +1 -0
- package/dist/define-factory.d.ts +113 -0
- package/dist/define-factory.js +247 -0
- package/dist/define-factory.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +164 -0
- package/dist/registry.js +277 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +281 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +80 -0
- package/dist/validation.js +217 -0
- package/dist/validation.js.map +1 -0
- package/package.json +63 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-block.js","sourceRoot":"","sources":["../src/define-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAmC,MAAM,KAAK,CAAA;AACxD,OAAO,EAAE,aAAa,EAAkB,MAAM,OAAO,CAAA;AAGrD,+DAA+D;AAC/D,aAAa;AACb,+DAA+D;AAE/D;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChD,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IAED,MAAM,cAAc,GAAG,+BAA+B,CAAA;IACtD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,oFAAoF,CACxG,CAAA;IACH,CAAC;IAED,oCAAoC;IACpC,MAAM,aAAa,GAAG;QACpB,WAAW;QACX,MAAM;QACN,MAAM;QACN,SAAS;QACT,QAAQ;QACR,OAAO;QACP,MAAM;QACN,OAAO;QACP,MAAM;QACN,MAAM;QACN,OAAO;QACP,MAAM;QACN,SAAS;QACT,MAAM;QACN,OAAO;QACP,QAAQ;QACR,SAAS;QACT,WAAW;QACX,MAAM;QACN,UAAU;QACV,OAAO;KACR,CAAA;IAED,IAAI,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,qDAAqD,CAAC,CAAA;IAC3F,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,IAAa,EAAE,IAAY;IAClD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACvE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,CAAC,IAAI,CACV,qBAAqB,IAAI,oBAAoB,OAAO,IAAI,4BAA4B;QAClF,8DAA8D,CACjE,CAAA;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAC3B,IAAY,EACZ,MAA0B,EAC1B,YAAqC;IAErC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;IAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7F,OAAO,CAAC,IAAI,CAAC,qBAAqB,IAAI,wCAAwC,MAAM,sBAAsB,CAAC,CAAA;IAC7G,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO,SAAS,YAAY;QAC1B,OAAO,aAAa,CAClB,KAAK,EACL;YACE,qBAAqB,EAAE,IAAI;YAC3B,KAAK,EAAE;gBACL,OAAO,EAAE,UAAU;gBACnB,MAAM,EAAE,oBAAoB;gBAC5B,YAAY,EAAE,KAAK;gBACnB,KAAK,EAAE,SAAS;gBAChB,IAAI,EAAE,4BAA4B;aACnC;SACF,EACD,UAAU,IAAI,oBAAoB,CACnC,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAED,+DAA+D;AAC/D,eAAe;AACf,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,UAAU,WAAW,CACzB,OAAoC;IAGpC,iEAAiE;IACjE,6EAA6E;IAC7E,+EAA+E;IAC/E,yDAAyD;IACzD,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5D,0EAA0E;IAC1E,IAAI,MAAM,GAAG,OAAO,CAAC,MAAuC,CAAA;IAC5D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CACV,qBAAqB,OAAO,CAAC,IAAI,0DAA0D;YACzF,gEAAgE,CACnE,CAAA;QACD,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAkC,CAAA;IACrE,CAAC;IAED,sCAAsC;IACtC,IAAI,YAAY,GAAG,OAAO,CAAC,YAAY,CAAA;IACvC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,qBAAqB,OAAO,CAAC,IAAI,gCAAgC,CAAC,CAAA;QAC/E,YAAY,GAAG,EAAY,CAAA;IAC7B,CAAC;IAED,gFAAgF;IAChF,IAAI,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;IACjC,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,qBAAqB,OAAO,CAAC,IAAI,4CAA4C,CAAC,CAAA;QAC3F,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAA6B,CAAA;IAC5E,CAAC;IAED,yCAAyC;IACzC,oBAAoB,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,CAAA;IAExD,qCAAqC;IACrC,MAAM,UAAU,GAAG;QACjB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS;QACvC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG;QACzB,MAAM;QACN,YAAY;QACZ,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;QACjD,SAAS;QACT,sDAAsD;QACtD,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC;QAC1F,GAAG,CAAC,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;QACvF,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QAC/D,GAAG,CAAC,OAAO,CAAC,kBAAkB,KAAK,SAAS,IAAI;YAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;SAC/C,CAAC;QACF,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;KACjD,CAAA;IAElC,kDAAkD;IAClD,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;AAClC,CAAC;AAcD;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAA2C;IACjF,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,2EAA2E;IAC3E,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAA;IACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC,CAAA;IAC1H,CAAC;SAAM,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,oCAAoC,EAAE,CAAC,CAAA;IAC7H,CAAC;SAAM,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,sBAAsB,EAAE,CAAC,CAAA;IAC/H,CAAC;IACD,oFAAoF;IACpF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAA;IACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC5D,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,8DAA8D,EAAE,CAAC,CAAA;IAC1J,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,+DAA+D;AAC/D,oBAAoB;AACpB,+DAA+D;AAE/D;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,GAAG,GAAG,KAAuC,CAAA;IACnD,OAAO,CACL,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC5B,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC5B,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAChC,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QAC9B,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ;QACpC,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CACpC,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAyC,EACzC,EAAU,EACV,KAAuB,EACvB,QAAkF;IAElF,MAAM,WAAW,GAAG;QAClB,GAAG,UAAU,CAAC,YAAY;QAC1B,GAAG,KAAK;KACC,CAAA;IAEX,wBAAwB;IACxB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAA;IACvD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7F,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,QAAQ,GAA4E;QACxF,EAAE;QACF,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,MAAM,CAAC,IAAc;KAC7B,CAAA;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,eAAe,UAAU,CAAC,IAAI,wBAAwB,CAAC,CAAA;QACzE,CAAC;QACD,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC9B,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["/**\n * defineBlock API\n *\n * Create type-safe custom block definitions with Zod schemas.\n *\n * @module @pwfabric/authoring/define-block\n *\n * @example\n * ```typescript\n * import { defineBlock } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * export const MyBlock = defineBlock({\n * type: 'my-block',\n * name: 'My Block',\n * schema: z.object({ title: z.string() }),\n * defaultProps: { title: 'Default' },\n * component: ({ props }) => <div>{props.title}</div>\n * })\n * ```\n */\n\nimport { z, type ZodSchema, type ZodTypeAny } from 'zod'\nimport { createElement, type ReactNode } from 'react'\nimport type { CustomBlockOptions, CustomBlockDefinition, SchemaProps } from './types.js'\n\n// ============================================================\n// VALIDATION\n// ============================================================\n\n/**\n * Validate block type identifier\n *\n * Must be kebab-case, 2-50 characters, start with letter.\n */\nfunction validateBlockType(type: string): void {\n if (typeof type !== 'string') {\n throw new Error('Block type must be a string')\n }\n\n if (type.length < 2 || type.length > 50) {\n throw new Error('Block type must be 2-50 characters')\n }\n\n const kebabCaseRegex = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/\n if (!kebabCaseRegex.test(type)) {\n throw new Error(\n `Block type \"${type}\" must be kebab-case (lowercase letters, numbers, hyphens, starting with a letter)`\n )\n }\n\n // Reserved types (core block types)\n const reservedTypes = [\n 'container',\n 'grid',\n 'text',\n 'heading',\n 'button',\n 'image',\n 'form',\n 'input',\n 'link',\n 'icon',\n 'badge',\n 'code',\n 'divider',\n 'list',\n 'table',\n 'avatar',\n 'tooltip',\n 'accordion',\n 'tabs',\n 'dropdown',\n 'modal',\n ]\n\n if (reservedTypes.includes(type)) {\n throw new Error(`Block type \"${type}\" is reserved. Use a unique type for custom blocks.`)\n }\n}\n\n/**\n * field-severity policy — `name` is a COSMETIC display label, NOT the\n * block's identity. It must never be a hard/fatal error: coerce a missing or\n * invalid name to the block `type` and warn. (`type` is the identity and stays\n * HARD — see validateBlockType.) This is the runtime SOFT half of the end-to-end\n * stitch; the authoring/install GATE applies the same policy via\n * validateBlockDefinition() below. Matches the already-tolerant render meta path\n * (installed-block-meta.descriptorToSnapshot), removing the \"valid-but-not-\n * renderable\" divergence where this throw crashed a block at render.\n */\nfunction coerceBlockName(name: unknown, type: string): string {\n if (typeof name === 'string' && name.length >= 1 && name.length <= 100) {\n return name\n }\n console.warn(\n `[pwfabric] block \"${type}\": invalid name (${typeof name}) — falling back to type. ` +\n `Names are cosmetic; fix at authoring, never fatal at render.`\n )\n return type\n}\n\n/**\n * Warn (never throw) when defaultProps don't satisfy the schema.\n * the renderer degrades, it does not crash. A mismatch is an authoring defect\n * to flag, not a render-time fatality (the registry already falls back to\n * defaultProps on a failed parse).\n */\nfunction warnOnSchemaMismatch(\n type: string,\n schema: ZodSchema<unknown>,\n defaultProps: Record<string, unknown>\n): void {\n const result = schema.safeParse(defaultProps)\n if (!result.success) {\n const errors = result.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ')\n console.warn(`[pwfabric] block \"${type}\": defaultProps do not match schema (${errors}). Fix at authoring.`)\n }\n}\n\n/**\n * render-degrade fallback: a block whose `component` is missing must\n * not white-screen the canvas. Render a visible placeholder instead of throwing.\n */\nfunction placeholderComponent(type: string): () => ReactNode {\n return function MissingBlock(): ReactNode {\n return createElement(\n 'div',\n {\n 'data-pw-placeholder': type,\n style: {\n padding: '8px 12px',\n border: '1px dashed #c0392b',\n borderRadius: '6px',\n color: '#c0392b',\n font: '12px system-ui, sans-serif',\n },\n },\n `Block \"${type}\" has no component`\n )\n }\n}\n\n// ============================================================\n// DEFINE BLOCK\n// ============================================================\n\n/**\n * Define a custom block with type-safe Zod schema validation\n *\n * Creates a reusable block definition that can be registered with\n * CustomBlockRegistry and used with SurfaceRenderer.\n *\n * @param options - Block definition options\n * @returns A frozen, type-safe custom block definition\n *\n * @example\n * ```typescript\n * import { defineBlock } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * // Define a pricing card block\n * const PricingCard = defineBlock({\n * type: 'pricing-card',\n * name: 'Pricing Card',\n * category: 'content',\n * schema: z.object({\n * title: z.string(),\n * price: z.number(),\n * currency: z.string().default('USD'),\n * features: z.array(z.string()),\n * highlighted: z.boolean().default(false),\n * }),\n * defaultProps: {\n * title: 'Basic Plan',\n * price: 9.99,\n * currency: 'USD',\n * features: ['Feature 1', 'Feature 2'],\n * highlighted: false,\n * },\n * component: ({ props }) => (\n * <div className={props.highlighted ? 'highlighted' : ''}>\n * <h3>{props.title}</h3>\n * <p>{props.currency} {props.price}</p>\n * <ul>\n * {props.features.map((f, i) => <li key={i}>{f}</li>)}\n * </ul>\n * </div>\n * ),\n * })\n * ```\n */\nexport function defineBlock<TSchema extends ZodTypeAny>(\n options: CustomBlockOptions<TSchema>\n): CustomBlockDefinition<SchemaProps<TSchema>> {\n type TProps = SchemaProps<TSchema>\n // the renderer DEGRADES, it never throws. `type` is the identity\n // → HARD (throws). Every other required field falls back + warns so a single\n // malformed block can't white-screen the canvas. The authoring/publish gate is\n // where these are rejected early; render stays graceful.\n validateBlockType(options.type)\n const safeName = coerceBlockName(options.name, options.type)\n\n // schema — missing → passthrough (props pass through unvalidated) + warn.\n let schema = options.schema as ZodSchema<TProps> | undefined\n if (!schema) {\n console.warn(\n `[pwfabric] block \"${options.type}\": no schema — using a passthrough (props unvalidated). ` +\n `Add a schema at authoring; this is degraded render, not a fix.`\n )\n schema = z.object({}).passthrough() as unknown as ZodSchema<TProps>\n }\n\n // defaultProps — missing → {} + warn.\n let defaultProps = options.defaultProps\n if (!defaultProps) {\n console.warn(`[pwfabric] block \"${options.type}\": no defaultProps — using {}.`)\n defaultProps = {} as TProps\n }\n\n // component — missing → visible placeholder + warn (not a thrown white-screen).\n let component = options.component\n if (typeof component !== 'function') {\n console.warn(`[pwfabric] block \"${options.type}\": no component — rendering a placeholder.`)\n component = placeholderComponent(options.type) as typeof options.component\n }\n\n // Defaults vs schema: warn, never throw.\n warnOnSchemaMismatch(options.type, schema, defaultProps)\n\n // Build the definition with defaults\n const definition = {\n type: options.type,\n name: safeName,\n category: options.category ?? 'content',\n icon: options.icon ?? '◆',\n schema,\n defaultProps,\n canHaveChildren: options.canHaveChildren ?? false,\n component,\n // Only include optional properties if they're defined\n ...(options.allowedChildren !== undefined && { allowedChildren: options.allowedChildren }),\n ...(options.allowedParents !== undefined && { allowedParents: options.allowedParents }),\n ...(options.traits !== undefined && { traits: options.traits }),\n ...(options.allowedChildTraits !== undefined && {\n allowedChildTraits: options.allowedChildTraits,\n }),\n ...(options.maxInstances !== undefined && { maxInstances: options.maxInstances }),\n } as CustomBlockDefinition<TProps>\n\n // Return a frozen definition to prevent mutations\n return Object.freeze(definition)\n}\n\n// ============================================================\n// DEFINITION GATE (single-source field policy)\n// ============================================================\n\n/** A reported issue from {@link validateBlockDefinition}. */\nexport interface BlockDefinitionIssue {\n field: 'type' | 'name'\n code: string\n severity: 'error' | 'warning'\n message: string\n}\n\n/**\n * Single source of truth for block-definition field severity, used by\n * the AUTHORING / INSTALL gate (packaging, manifest validation, host\n * install). Non-throwing reporter — the SAME stitch defineBlock applies at\n * runtime, surfaced as structured issues so callers can REJECT identity\n * problems (`type`, severity=error) and merely WARN on cosmetic ones (`name`,\n * severity=warning). Keeping this the one place the policy lives is what makes\n * the treatment identical end-to-end (authoring → install → runtime → render).\n */\nexport function validateBlockDefinition(options: { type?: unknown; name?: unknown }): BlockDefinitionIssue[] {\n const issues: BlockDefinitionIssue[] = []\n // type — block IDENTITY → HARD (error). Mirrors validateBlockType's rules.\n const t = options.type\n if (typeof t !== 'string') {\n issues.push({ field: 'type', code: 'BLOCK_TYPE_NOT_STRING', severity: 'error', message: 'Block type must be a string' })\n } else if (t.length < 2 || t.length > 50) {\n issues.push({ field: 'type', code: 'BLOCK_TYPE_LENGTH', severity: 'error', message: 'Block type must be 2-50 characters' })\n } else if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(t)) {\n issues.push({ field: 'type', code: 'BLOCK_TYPE_FORMAT', severity: 'error', message: `Block type \"${t}\" must be kebab-case` })\n }\n // name — cosmetic label → SOFT (warning). Never blocks; coerced to type at runtime.\n const n = options.name\n if (typeof n !== 'string' || n.length < 1 || n.length > 100) {\n issues.push({ field: 'name', code: 'BLOCK_NAME_INVALID', severity: 'warning', message: 'Block name should be a 1-100 char string; falls back to type' })\n }\n return issues\n}\n\n// ============================================================\n// UTILITY FUNCTIONS\n// ============================================================\n\n/**\n * Check if a value is a custom block definition\n */\nexport function isCustomBlockDefinition(value: unknown): value is CustomBlockDefinition {\n if (!value || typeof value !== 'object') {\n return false\n }\n\n const def = value as Partial<CustomBlockDefinition>\n return (\n typeof def.type === 'string' &&\n typeof def.name === 'string' &&\n typeof def.category === 'string' &&\n typeof def.schema === 'object' &&\n typeof def.defaultProps === 'object' &&\n typeof def.component === 'function'\n )\n}\n\n/**\n * Create a block instance from a custom block definition\n *\n * @param definition - The block definition\n * @param id - Unique block ID\n * @param props - Optional props override (merged with defaults)\n * @param children - Optional children blocks\n */\nexport function createBlockInstance<TProps extends Record<string, unknown>>(\n definition: CustomBlockDefinition<TProps>,\n id: string,\n props?: Partial<TProps>,\n children?: readonly { id: string; type: string; props: Record<string, unknown> }[]\n): { id: string; type: string; props: TProps; children?: typeof children } {\n const mergedProps = {\n ...definition.defaultProps,\n ...props,\n } as TProps\n\n // Validate merged props\n const result = definition.schema.safeParse(mergedProps)\n if (!result.success) {\n const errors = result.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ')\n throw new Error(`Invalid block props: ${errors}`)\n }\n\n const instance: { id: string; type: string; props: TProps; children?: typeof children } = {\n id,\n type: definition.type,\n props: result.data as TProps,\n }\n\n if (children && children.length > 0) {\n if (!definition.canHaveChildren) {\n throw new Error(`Block type \"${definition.type}\" cannot have children`)\n }\n instance.children = children\n }\n\n return instance\n}\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineFactory API
|
|
3
|
+
*
|
|
4
|
+
* Create factory functions that generate block compositions.
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/authoring/define-factory
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { defineFactory } from '@pwfabric/authoring'
|
|
11
|
+
* import { z } from 'zod'
|
|
12
|
+
*
|
|
13
|
+
* const MyPageFactory = defineFactory({
|
|
14
|
+
* id: 'my-page',
|
|
15
|
+
* name: 'My Page Factory',
|
|
16
|
+
* configSchema: z.object({
|
|
17
|
+
* title: z.string(),
|
|
18
|
+
* subtitle: z.string().optional(),
|
|
19
|
+
* }),
|
|
20
|
+
* build: (config) => [
|
|
21
|
+
* { type: 'heading', props: { text: config.title, level: 'h1' } },
|
|
22
|
+
* { type: 'text', props: { content: config.subtitle ?? '' } },
|
|
23
|
+
* ]
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { Block } from '@pwfabric/core';
|
|
28
|
+
import type { FactoryOptions, FactoryDefinition, FactoryConfig } from './types.js';
|
|
29
|
+
/**
|
|
30
|
+
* Reset the ID counter (useful for testing)
|
|
31
|
+
*/
|
|
32
|
+
export declare function resetIdCounter(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Define a factory function that generates block compositions
|
|
35
|
+
*
|
|
36
|
+
* Factories allow you to create reusable block patterns that can be
|
|
37
|
+
* customized with configuration. They automatically generate unique
|
|
38
|
+
* block IDs and validate configuration against an optional schema.
|
|
39
|
+
*
|
|
40
|
+
* @param options - Factory definition options
|
|
41
|
+
* @returns A frozen factory definition
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { defineFactory } from '@pwfabric/authoring'
|
|
46
|
+
* import { z } from 'zod'
|
|
47
|
+
*
|
|
48
|
+
* // Define a pricing section factory
|
|
49
|
+
* const PricingSectionFactory = defineFactory({
|
|
50
|
+
* id: 'pricing-section',
|
|
51
|
+
* name: 'Pricing Section',
|
|
52
|
+
* description: 'Create a pricing section with multiple tiers',
|
|
53
|
+
* configSchema: z.object({
|
|
54
|
+
* title: z.string(),
|
|
55
|
+
* tiers: z.array(z.object({
|
|
56
|
+
* name: z.string(),
|
|
57
|
+
* price: z.number(),
|
|
58
|
+
* features: z.array(z.string()),
|
|
59
|
+
* })),
|
|
60
|
+
* }),
|
|
61
|
+
* build: (config) => [
|
|
62
|
+
* {
|
|
63
|
+
* type: 'container',
|
|
64
|
+
* props: { direction: 'column', gap: 32 },
|
|
65
|
+
* children: [
|
|
66
|
+
* { type: 'heading', props: { text: config.title, level: 'h2' } },
|
|
67
|
+
* {
|
|
68
|
+
* type: 'grid',
|
|
69
|
+
* props: { columns: config.tiers.length },
|
|
70
|
+
* children: config.tiers.map((tier) => ({
|
|
71
|
+
* type: 'pricing-card',
|
|
72
|
+
* props: {
|
|
73
|
+
* title: tier.name,
|
|
74
|
+
* price: tier.price,
|
|
75
|
+
* features: tier.features,
|
|
76
|
+
* },
|
|
77
|
+
* })),
|
|
78
|
+
* },
|
|
79
|
+
* ],
|
|
80
|
+
* },
|
|
81
|
+
* ],
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* // Use the factory
|
|
85
|
+
* const blocks = PricingSectionFactory.build({
|
|
86
|
+
* title: 'Choose Your Plan',
|
|
87
|
+
* tiers: [
|
|
88
|
+
* { name: 'Basic', price: 9, features: ['Feature 1'] },
|
|
89
|
+
* { name: 'Pro', price: 29, features: ['Feature 1', 'Feature 2'] },
|
|
90
|
+
* ],
|
|
91
|
+
* })
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function defineFactory<TConfig extends FactoryConfig = FactoryConfig>(options: FactoryOptions<TConfig>): FactoryDefinition<TConfig>;
|
|
95
|
+
/**
|
|
96
|
+
* Check if a value is a factory definition
|
|
97
|
+
*/
|
|
98
|
+
export declare function isFactoryDefinition(value: unknown): value is FactoryDefinition;
|
|
99
|
+
/**
|
|
100
|
+
* Compose multiple factory outputs into a single block array
|
|
101
|
+
*
|
|
102
|
+
* @param factories - Array of factory output block arrays
|
|
103
|
+
* @returns Combined block array
|
|
104
|
+
*/
|
|
105
|
+
export declare function composeFactoryOutputs(...factories: readonly (readonly Block[])[]): readonly Block[];
|
|
106
|
+
/**
|
|
107
|
+
* Wrap factory output in a container block
|
|
108
|
+
*
|
|
109
|
+
* @param blocks - Factory output blocks
|
|
110
|
+
* @param containerProps - Props for the wrapper container
|
|
111
|
+
* @returns Single container block with children
|
|
112
|
+
*/
|
|
113
|
+
export declare function wrapInContainer(blocks: readonly Block[], containerProps?: Record<string, unknown>): Block;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineFactory API
|
|
3
|
+
*
|
|
4
|
+
* Create factory functions that generate block compositions.
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/authoring/define-factory
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { defineFactory } from '@pwfabric/authoring'
|
|
11
|
+
* import { z } from 'zod'
|
|
12
|
+
*
|
|
13
|
+
* const MyPageFactory = defineFactory({
|
|
14
|
+
* id: 'my-page',
|
|
15
|
+
* name: 'My Page Factory',
|
|
16
|
+
* configSchema: z.object({
|
|
17
|
+
* title: z.string(),
|
|
18
|
+
* subtitle: z.string().optional(),
|
|
19
|
+
* }),
|
|
20
|
+
* build: (config) => [
|
|
21
|
+
* { type: 'heading', props: { text: config.title, level: 'h1' } },
|
|
22
|
+
* { type: 'text', props: { content: config.subtitle ?? '' } },
|
|
23
|
+
* ]
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { DEFAULT_BLOCK_LAYOUT } from '@pwfabric/core';
|
|
28
|
+
import { blockRegistry } from '@pwfabric/runtime/blocks';
|
|
29
|
+
// ============================================================
|
|
30
|
+
// ID GENERATION
|
|
31
|
+
// ============================================================
|
|
32
|
+
let idCounter = 0;
|
|
33
|
+
/**
|
|
34
|
+
* Generate a unique block ID
|
|
35
|
+
*/
|
|
36
|
+
function generateBlockId(type) {
|
|
37
|
+
idCounter += 1;
|
|
38
|
+
return `${type}-${idCounter}-${Date.now().toString(36)}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reset the ID counter (useful for testing)
|
|
42
|
+
*/
|
|
43
|
+
export function resetIdCounter() {
|
|
44
|
+
idCounter = 0;
|
|
45
|
+
}
|
|
46
|
+
// ============================================================
|
|
47
|
+
// VALIDATION
|
|
48
|
+
// ============================================================
|
|
49
|
+
/**
|
|
50
|
+
* Validate factory identifier
|
|
51
|
+
*/
|
|
52
|
+
function validateFactoryId(id) {
|
|
53
|
+
if (typeof id !== 'string') {
|
|
54
|
+
throw new Error('Factory id must be a string');
|
|
55
|
+
}
|
|
56
|
+
if (id.length < 2 || id.length > 50) {
|
|
57
|
+
throw new Error('Factory id must be 2-50 characters');
|
|
58
|
+
}
|
|
59
|
+
const kebabCaseRegex = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
60
|
+
if (!kebabCaseRegex.test(id)) {
|
|
61
|
+
throw new Error(`Factory id "${id}" must be kebab-case (lowercase letters, numbers, hyphens, starting with a letter)`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate factory config against schema
|
|
66
|
+
*/
|
|
67
|
+
function validateConfig(config, schema) {
|
|
68
|
+
if (!schema) {
|
|
69
|
+
// No schema, accept any config
|
|
70
|
+
return {
|
|
71
|
+
valid: true,
|
|
72
|
+
config: config,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const result = schema.safeParse(config);
|
|
76
|
+
if (result.success) {
|
|
77
|
+
return {
|
|
78
|
+
valid: true,
|
|
79
|
+
config: result.data,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const errors = result.error.errors.map((e) => ({
|
|
83
|
+
path: e.path.join('.'),
|
|
84
|
+
message: e.message,
|
|
85
|
+
}));
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
errors,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// ============================================================
|
|
92
|
+
// BLOCK SPEC TO BLOCK
|
|
93
|
+
// ============================================================
|
|
94
|
+
/**
|
|
95
|
+
* Convert a BlockSpec (without id) to a Block (with id)
|
|
96
|
+
*/
|
|
97
|
+
function specToBlock(spec) {
|
|
98
|
+
const block = {
|
|
99
|
+
id: generateBlockId(spec.type),
|
|
100
|
+
type: spec.type,
|
|
101
|
+
props: spec.props,
|
|
102
|
+
layout: blockRegistry.get(spec.type)?.defaultLayout ?? DEFAULT_BLOCK_LAYOUT,
|
|
103
|
+
};
|
|
104
|
+
if (spec.children && spec.children.length > 0) {
|
|
105
|
+
return {
|
|
106
|
+
...block,
|
|
107
|
+
children: spec.children.map(specToBlock),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return block;
|
|
111
|
+
}
|
|
112
|
+
// ============================================================
|
|
113
|
+
// DEFINE FACTORY
|
|
114
|
+
// ============================================================
|
|
115
|
+
/**
|
|
116
|
+
* Define a factory function that generates block compositions
|
|
117
|
+
*
|
|
118
|
+
* Factories allow you to create reusable block patterns that can be
|
|
119
|
+
* customized with configuration. They automatically generate unique
|
|
120
|
+
* block IDs and validate configuration against an optional schema.
|
|
121
|
+
*
|
|
122
|
+
* @param options - Factory definition options
|
|
123
|
+
* @returns A frozen factory definition
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* import { defineFactory } from '@pwfabric/authoring'
|
|
128
|
+
* import { z } from 'zod'
|
|
129
|
+
*
|
|
130
|
+
* // Define a pricing section factory
|
|
131
|
+
* const PricingSectionFactory = defineFactory({
|
|
132
|
+
* id: 'pricing-section',
|
|
133
|
+
* name: 'Pricing Section',
|
|
134
|
+
* description: 'Create a pricing section with multiple tiers',
|
|
135
|
+
* configSchema: z.object({
|
|
136
|
+
* title: z.string(),
|
|
137
|
+
* tiers: z.array(z.object({
|
|
138
|
+
* name: z.string(),
|
|
139
|
+
* price: z.number(),
|
|
140
|
+
* features: z.array(z.string()),
|
|
141
|
+
* })),
|
|
142
|
+
* }),
|
|
143
|
+
* build: (config) => [
|
|
144
|
+
* {
|
|
145
|
+
* type: 'container',
|
|
146
|
+
* props: { direction: 'column', gap: 32 },
|
|
147
|
+
* children: [
|
|
148
|
+
* { type: 'heading', props: { text: config.title, level: 'h2' } },
|
|
149
|
+
* {
|
|
150
|
+
* type: 'grid',
|
|
151
|
+
* props: { columns: config.tiers.length },
|
|
152
|
+
* children: config.tiers.map((tier) => ({
|
|
153
|
+
* type: 'pricing-card',
|
|
154
|
+
* props: {
|
|
155
|
+
* title: tier.name,
|
|
156
|
+
* price: tier.price,
|
|
157
|
+
* features: tier.features,
|
|
158
|
+
* },
|
|
159
|
+
* })),
|
|
160
|
+
* },
|
|
161
|
+
* ],
|
|
162
|
+
* },
|
|
163
|
+
* ],
|
|
164
|
+
* })
|
|
165
|
+
*
|
|
166
|
+
* // Use the factory
|
|
167
|
+
* const blocks = PricingSectionFactory.build({
|
|
168
|
+
* title: 'Choose Your Plan',
|
|
169
|
+
* tiers: [
|
|
170
|
+
* { name: 'Basic', price: 9, features: ['Feature 1'] },
|
|
171
|
+
* { name: 'Pro', price: 29, features: ['Feature 1', 'Feature 2'] },
|
|
172
|
+
* ],
|
|
173
|
+
* })
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export function defineFactory(options) {
|
|
177
|
+
// Validate required fields
|
|
178
|
+
validateFactoryId(options.id);
|
|
179
|
+
if (typeof options.build !== 'function') {
|
|
180
|
+
throw new Error('Factory build function is required');
|
|
181
|
+
}
|
|
182
|
+
// Create the build wrapper that converts specs to blocks
|
|
183
|
+
const buildWithIds = (config) => {
|
|
184
|
+
const specs = options.build(config);
|
|
185
|
+
return specs.map(specToBlock);
|
|
186
|
+
};
|
|
187
|
+
// Create the validation function
|
|
188
|
+
const validateConfigFn = (config) => {
|
|
189
|
+
return validateConfig(config, options.configSchema);
|
|
190
|
+
};
|
|
191
|
+
const definition = {
|
|
192
|
+
id: options.id,
|
|
193
|
+
name: options.name ?? options.id,
|
|
194
|
+
description: options.description ?? '',
|
|
195
|
+
build: buildWithIds,
|
|
196
|
+
validateConfig: validateConfigFn,
|
|
197
|
+
// Only include configSchema if it's defined
|
|
198
|
+
...(options.configSchema !== undefined && { configSchema: options.configSchema }),
|
|
199
|
+
};
|
|
200
|
+
return Object.freeze(definition);
|
|
201
|
+
}
|
|
202
|
+
// ============================================================
|
|
203
|
+
// UTILITY FUNCTIONS
|
|
204
|
+
// ============================================================
|
|
205
|
+
/**
|
|
206
|
+
* Check if a value is a factory definition
|
|
207
|
+
*/
|
|
208
|
+
export function isFactoryDefinition(value) {
|
|
209
|
+
if (!value || typeof value !== 'object') {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
const def = value;
|
|
213
|
+
return (typeof def.id === 'string' &&
|
|
214
|
+
typeof def.name === 'string' &&
|
|
215
|
+
typeof def.build === 'function' &&
|
|
216
|
+
typeof def.validateConfig === 'function');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Compose multiple factory outputs into a single block array
|
|
220
|
+
*
|
|
221
|
+
* @param factories - Array of factory output block arrays
|
|
222
|
+
* @returns Combined block array
|
|
223
|
+
*/
|
|
224
|
+
export function composeFactoryOutputs(...factories) {
|
|
225
|
+
return factories.flat();
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Wrap factory output in a container block
|
|
229
|
+
*
|
|
230
|
+
* @param blocks - Factory output blocks
|
|
231
|
+
* @param containerProps - Props for the wrapper container
|
|
232
|
+
* @returns Single container block with children
|
|
233
|
+
*/
|
|
234
|
+
export function wrapInContainer(blocks, containerProps = {}) {
|
|
235
|
+
return {
|
|
236
|
+
id: generateBlockId('container'),
|
|
237
|
+
type: 'container',
|
|
238
|
+
props: {
|
|
239
|
+
direction: 'column',
|
|
240
|
+
gap: 24,
|
|
241
|
+
...containerProps,
|
|
242
|
+
},
|
|
243
|
+
layout: blockRegistry.get('container')?.defaultLayout ?? DEFAULT_BLOCK_LAYOUT,
|
|
244
|
+
children: [...blocks],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=define-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-factory.js","sourceRoot":"","sources":["../src/define-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAUxD,+DAA+D;AAC/D,gBAAgB;AAChB,+DAA+D;AAE/D,IAAI,SAAS,GAAG,CAAC,CAAA;AAEjB;;GAEG;AACH,SAAS,eAAe,CAAC,IAAY;IACnC,SAAS,IAAI,CAAC,CAAA;IACd,OAAO,GAAG,IAAI,IAAI,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAA;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,SAAS,GAAG,CAAC,CAAA;AACf,CAAC;AAED,+DAA+D;AAC/D,aAAa;AACb,+DAA+D;AAE/D;;GAEG;AACH,SAAS,iBAAiB,CAAC,EAAU;IACnC,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChD,CAAC;IAED,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IAED,MAAM,cAAc,GAAG,+BAA+B,CAAA;IACtD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,eAAe,EAAE,oFAAoF,CACtG,CAAA;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CACrB,MAAe,EACf,MAA2B;IAE3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,+BAA+B;QAC/B,OAAO;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,MAAiB;SAC1B,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACvC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,MAAM,CAAC,IAAI;SACpB,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAA6B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,OAAO,EAAE,CAAC,CAAC,OAAO;KACnB,CAAC,CAAC,CAAA;IAEH,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,MAAM;KACP,CAAA;AACH,CAAC;AAED,+DAA+D;AAC/D,sBAAsB;AACtB,+DAA+D;AAE/D;;GAEG;AACH,SAAS,WAAW,CAAC,IAAe;IAClC,MAAM,KAAK,GAAU;QACnB,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,aAAa,IAAI,oBAAoB;KAC5E,CAAA;IAED,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,GAAG,KAAK;YACR,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;SACzC,CAAA;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED,+DAA+D;AAC/D,iBAAiB;AACjB,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAgC;IAEhC,2BAA2B;IAC3B,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAE7B,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IAED,yDAAyD;IACzD,MAAM,YAAY,GAAG,CAAC,MAAe,EAAoB,EAAE;QACzD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACnC,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAA;IAED,iCAAiC;IACjC,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAoC,EAAE;QAC7E,OAAO,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IACrD,CAAC,CAAA;IAED,MAAM,UAAU,GAAG;QACjB,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE;QAChC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,gBAAgB;QAChC,4CAA4C;QAC5C,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;KACpD,CAAA;IAE/B,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;AAClC,CAAC;AAED,+DAA+D;AAC/D,oBAAoB;AACpB,+DAA+D;AAE/D;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,GAAG,GAAG,KAAmC,CAAA;IAC/C,OAAO,CACL,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ;QAC1B,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC5B,OAAO,GAAG,CAAC,KAAK,KAAK,UAAU;QAC/B,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,CACzC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAG,SAAwC;IAE3C,OAAO,SAAS,CAAC,IAAI,EAAE,CAAA;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAwB,EACxB,iBAA0C,EAAE;IAE5C,OAAO;QACL,EAAE,EAAE,eAAe,CAAC,WAAW,CAAC;QAChC,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE;YACL,SAAS,EAAE,QAAQ;YACnB,GAAG,EAAE,EAAE;YACP,GAAG,cAAc;SAClB;QACD,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,aAAa,IAAI,oBAAoB;QAC7E,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC;KACtB,CAAA;AACH,CAAC","sourcesContent":["/**\n * defineFactory API\n *\n * Create factory functions that generate block compositions.\n *\n * @module @pwfabric/authoring/define-factory\n *\n * @example\n * ```typescript\n * import { defineFactory } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * const MyPageFactory = defineFactory({\n * id: 'my-page',\n * name: 'My Page Factory',\n * configSchema: z.object({\n * title: z.string(),\n * subtitle: z.string().optional(),\n * }),\n * build: (config) => [\n * { type: 'heading', props: { text: config.title, level: 'h1' } },\n * { type: 'text', props: { content: config.subtitle ?? '' } },\n * ]\n * })\n * ```\n */\n\nimport type { ZodSchema } from 'zod'\nimport type { Block } from '@pwfabric/core'\nimport { DEFAULT_BLOCK_LAYOUT } from '@pwfabric/core'\nimport { blockRegistry } from '@pwfabric/runtime/blocks'\nimport type {\n FactoryOptions,\n FactoryDefinition,\n FactoryConfig,\n FactoryValidationResult,\n FactoryValidationError,\n BlockSpec,\n} from './types.js'\n\n// ============================================================\n// ID GENERATION\n// ============================================================\n\nlet idCounter = 0\n\n/**\n * Generate a unique block ID\n */\nfunction generateBlockId(type: string): string {\n idCounter += 1\n return `${type}-${idCounter}-${Date.now().toString(36)}`\n}\n\n/**\n * Reset the ID counter (useful for testing)\n */\nexport function resetIdCounter(): void {\n idCounter = 0\n}\n\n// ============================================================\n// VALIDATION\n// ============================================================\n\n/**\n * Validate factory identifier\n */\nfunction validateFactoryId(id: string): void {\n if (typeof id !== 'string') {\n throw new Error('Factory id must be a string')\n }\n\n if (id.length < 2 || id.length > 50) {\n throw new Error('Factory id must be 2-50 characters')\n }\n\n const kebabCaseRegex = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/\n if (!kebabCaseRegex.test(id)) {\n throw new Error(\n `Factory id \"${id}\" must be kebab-case (lowercase letters, numbers, hyphens, starting with a letter)`\n )\n }\n}\n\n/**\n * Validate factory config against schema\n */\nfunction validateConfig<TConfig extends FactoryConfig>(\n config: unknown,\n schema?: ZodSchema<TConfig>\n): FactoryValidationResult<TConfig> {\n if (!schema) {\n // No schema, accept any config\n return {\n valid: true,\n config: config as TConfig,\n }\n }\n\n const result = schema.safeParse(config)\n if (result.success) {\n return {\n valid: true,\n config: result.data,\n }\n }\n\n const errors: FactoryValidationError[] = result.error.errors.map((e) => ({\n path: e.path.join('.'),\n message: e.message,\n }))\n\n return {\n valid: false,\n errors,\n }\n}\n\n// ============================================================\n// BLOCK SPEC TO BLOCK\n// ============================================================\n\n/**\n * Convert a BlockSpec (without id) to a Block (with id)\n */\nfunction specToBlock(spec: BlockSpec): Block {\n const block: Block = {\n id: generateBlockId(spec.type),\n type: spec.type,\n props: spec.props,\n layout: blockRegistry.get(spec.type)?.defaultLayout ?? DEFAULT_BLOCK_LAYOUT,\n }\n\n if (spec.children && spec.children.length > 0) {\n return {\n ...block,\n children: spec.children.map(specToBlock),\n }\n }\n\n return block\n}\n\n// ============================================================\n// DEFINE FACTORY\n// ============================================================\n\n/**\n * Define a factory function that generates block compositions\n *\n * Factories allow you to create reusable block patterns that can be\n * customized with configuration. They automatically generate unique\n * block IDs and validate configuration against an optional schema.\n *\n * @param options - Factory definition options\n * @returns A frozen factory definition\n *\n * @example\n * ```typescript\n * import { defineFactory } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * // Define a pricing section factory\n * const PricingSectionFactory = defineFactory({\n * id: 'pricing-section',\n * name: 'Pricing Section',\n * description: 'Create a pricing section with multiple tiers',\n * configSchema: z.object({\n * title: z.string(),\n * tiers: z.array(z.object({\n * name: z.string(),\n * price: z.number(),\n * features: z.array(z.string()),\n * })),\n * }),\n * build: (config) => [\n * {\n * type: 'container',\n * props: { direction: 'column', gap: 32 },\n * children: [\n * { type: 'heading', props: { text: config.title, level: 'h2' } },\n * {\n * type: 'grid',\n * props: { columns: config.tiers.length },\n * children: config.tiers.map((tier) => ({\n * type: 'pricing-card',\n * props: {\n * title: tier.name,\n * price: tier.price,\n * features: tier.features,\n * },\n * })),\n * },\n * ],\n * },\n * ],\n * })\n *\n * // Use the factory\n * const blocks = PricingSectionFactory.build({\n * title: 'Choose Your Plan',\n * tiers: [\n * { name: 'Basic', price: 9, features: ['Feature 1'] },\n * { name: 'Pro', price: 29, features: ['Feature 1', 'Feature 2'] },\n * ],\n * })\n * ```\n */\nexport function defineFactory<TConfig extends FactoryConfig = FactoryConfig>(\n options: FactoryOptions<TConfig>\n): FactoryDefinition<TConfig> {\n // Validate required fields\n validateFactoryId(options.id)\n\n if (typeof options.build !== 'function') {\n throw new Error('Factory build function is required')\n }\n\n // Create the build wrapper that converts specs to blocks\n const buildWithIds = (config: TConfig): readonly Block[] => {\n const specs = options.build(config)\n return specs.map(specToBlock)\n }\n\n // Create the validation function\n const validateConfigFn = (config: unknown): FactoryValidationResult<TConfig> => {\n return validateConfig(config, options.configSchema)\n }\n\n const definition = {\n id: options.id,\n name: options.name ?? options.id,\n description: options.description ?? '',\n build: buildWithIds,\n validateConfig: validateConfigFn,\n // Only include configSchema if it's defined\n ...(options.configSchema !== undefined && { configSchema: options.configSchema }),\n } as FactoryDefinition<TConfig>\n\n return Object.freeze(definition)\n}\n\n// ============================================================\n// UTILITY FUNCTIONS\n// ============================================================\n\n/**\n * Check if a value is a factory definition\n */\nexport function isFactoryDefinition(value: unknown): value is FactoryDefinition {\n if (!value || typeof value !== 'object') {\n return false\n }\n\n const def = value as Partial<FactoryDefinition>\n return (\n typeof def.id === 'string' &&\n typeof def.name === 'string' &&\n typeof def.build === 'function' &&\n typeof def.validateConfig === 'function'\n )\n}\n\n/**\n * Compose multiple factory outputs into a single block array\n *\n * @param factories - Array of factory output block arrays\n * @returns Combined block array\n */\nexport function composeFactoryOutputs(\n ...factories: readonly (readonly Block[])[]\n): readonly Block[] {\n return factories.flat()\n}\n\n/**\n * Wrap factory output in a container block\n *\n * @param blocks - Factory output blocks\n * @param containerProps - Props for the wrapper container\n * @returns Single container block with children\n */\nexport function wrapInContainer(\n blocks: readonly Block[],\n containerProps: Record<string, unknown> = {}\n): Block {\n return {\n id: generateBlockId('container'),\n type: 'container',\n props: {\n direction: 'column',\n gap: 24,\n ...containerProps,\n },\n layout: blockRegistry.get('container')?.defaultLayout ?? DEFAULT_BLOCK_LAYOUT,\n children: [...blocks],\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pwfabric/authoring
|
|
3
|
+
*
|
|
4
|
+
* The surface a pack author writes against: define a block, compose a factory,
|
|
5
|
+
* declare a capability, register and validate what you built.
|
|
6
|
+
*
|
|
7
|
+
* It holds no HTTP client: talking to a hosting service is that service's
|
|
8
|
+
* own package. The scaffolding templates in the authoring tooling emit
|
|
9
|
+
* `import { defineBlock } from ...`, so this is the module name a third-party
|
|
10
|
+
* author reads first.
|
|
11
|
+
*
|
|
12
|
+
* @module @pwfabric/authoring
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { defineBlock, createCustomBlockRegistry } from '@pwfabric/authoring'
|
|
17
|
+
* import { z } from 'zod'
|
|
18
|
+
*
|
|
19
|
+
* const MyBlock = defineBlock({
|
|
20
|
+
* type: 'my-block',
|
|
21
|
+
* name: 'My Block',
|
|
22
|
+
* schema: z.object({ title: z.string() }),
|
|
23
|
+
* defaultProps: { title: 'Default' },
|
|
24
|
+
* component: ({ props }) => <div>{props.title}</div>
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* const registry = createCustomBlockRegistry()
|
|
28
|
+
* registry.register(MyBlock)
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export * from '@pwfabric/runtime';
|
|
32
|
+
export type { CustomBlockOptions, SchemaProps, CustomBlockComponentProps, CustomBlockComponent, CustomBlockContext, CustomBlockDefinition, FactoryConfig, BlockSpec, FactoryOptions, FactoryDefinition, FactoryValidationResult, FactoryValidationError, ICustomBlockRegistry, BlockValidationResult, BlockValidationError, PropsValidationResult, InferBlockProps, InferFactoryConfig, InferSchemaProps, } from './types.js';
|
|
33
|
+
export { defineBlock, isCustomBlockDefinition, createBlockInstance } from './define-block.js';
|
|
34
|
+
export { validateBlockDefinition } from './define-block.js';
|
|
35
|
+
export type { BlockDefinitionIssue } from './define-block.js';
|
|
36
|
+
export { defineFactory, isFactoryDefinition, composeFactoryOutputs, wrapInContainer, resetIdCounter, } from './define-factory.js';
|
|
37
|
+
export { CapabilityBuilder, defineCapability } from '@pwfabric/contracts';
|
|
38
|
+
export { CustomBlockRegistry, createCustomBlockRegistry, getCustomBlockRegistry, resetGlobalRegistry, mergeRegistries, mergeWithBaseComponents, } from './registry.js';
|
|
39
|
+
export { validateProps, validatePropsWithDefaults, validateBlock, validateBlockTree, createBlockValidator, createPropsValidator, formatValidationErrors, hasErrors, isBlock, isBlockArray, } from './validation.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pwfabric/authoring
|
|
3
|
+
*
|
|
4
|
+
* The surface a pack author writes against: define a block, compose a factory,
|
|
5
|
+
* declare a capability, register and validate what you built.
|
|
6
|
+
*
|
|
7
|
+
* It holds no HTTP client: talking to a hosting service is that service's
|
|
8
|
+
* own package. The scaffolding templates in the authoring tooling emit
|
|
9
|
+
* `import { defineBlock } from ...`, so this is the module name a third-party
|
|
10
|
+
* author reads first.
|
|
11
|
+
*
|
|
12
|
+
* @module @pwfabric/authoring
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { defineBlock, createCustomBlockRegistry } from '@pwfabric/authoring'
|
|
17
|
+
* import { z } from 'zod'
|
|
18
|
+
*
|
|
19
|
+
* const MyBlock = defineBlock({
|
|
20
|
+
* type: 'my-block',
|
|
21
|
+
* name: 'My Block',
|
|
22
|
+
* schema: z.object({ title: z.string() }),
|
|
23
|
+
* defaultProps: { title: 'Default' },
|
|
24
|
+
* component: ({ props }) => <div>{props.title}</div>
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* const registry = createCustomBlockRegistry()
|
|
28
|
+
* registry.register(MyBlock)
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
// Re-export runtime (which includes core and contracts)
|
|
32
|
+
export * from '@pwfabric/runtime';
|
|
33
|
+
// ============================================================
|
|
34
|
+
// DEFINE BLOCK API
|
|
35
|
+
// ============================================================
|
|
36
|
+
export { defineBlock, isCustomBlockDefinition, createBlockInstance } from './define-block.js';
|
|
37
|
+
// single-source field-severity gate for authoring/install.
|
|
38
|
+
export { validateBlockDefinition } from './define-block.js';
|
|
39
|
+
// ============================================================
|
|
40
|
+
// DEFINE FACTORY API
|
|
41
|
+
// ============================================================
|
|
42
|
+
export { defineFactory, isFactoryDefinition, composeFactoryOutputs, wrapInContainer, resetIdCounter, } from './define-factory.js';
|
|
43
|
+
// ============================================================
|
|
44
|
+
// DEFINE CAPABILITY API
|
|
45
|
+
// ============================================================
|
|
46
|
+
export { CapabilityBuilder, defineCapability } from '@pwfabric/contracts';
|
|
47
|
+
// ============================================================
|
|
48
|
+
// REGISTRY API
|
|
49
|
+
// ============================================================
|
|
50
|
+
export { CustomBlockRegistry, createCustomBlockRegistry, getCustomBlockRegistry, resetGlobalRegistry, mergeRegistries, mergeWithBaseComponents, } from './registry.js';
|
|
51
|
+
// ============================================================
|
|
52
|
+
// VALIDATION API
|
|
53
|
+
// ============================================================
|
|
54
|
+
export { validateProps, validatePropsWithDefaults, validateBlock, validateBlockTree, createBlockValidator, createPropsValidator, formatValidationErrors, hasErrors, isBlock, isBlockArray, } from './validation.js';
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,wDAAwD;AACxD,cAAc,mBAAmB,CAAA;AAqCjC,+DAA+D;AAC/D,mBAAmB;AACnB,+DAA+D;AAE/D,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC7F,2DAA2D;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAG3D,+DAA+D;AAC/D,qBAAqB;AACrB,+DAA+D;AAE/D,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAA;AAE5B,+DAA+D;AAC/D,wBAAwB;AACxB,+DAA+D;AAE/D,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEzE,+DAA+D;AAC/D,eAAe;AACf,+DAA+D;AAE/D,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAEtB,+DAA+D;AAC/D,iBAAiB;AACjB,+DAA+D;AAE/D,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,SAAS,EACT,OAAO,EACP,YAAY,GACb,MAAM,iBAAiB,CAAA","sourcesContent":["/**\n * @pwfabric/authoring\n *\n * The surface a pack author writes against: define a block, compose a factory,\n * declare a capability, register and validate what you built.\n *\n * It holds no HTTP client: talking to a hosting service is that service's\n * own package. The scaffolding templates in the authoring tooling emit\n * `import { defineBlock } from ...`, so this is the module name a third-party\n * author reads first.\n *\n * @module @pwfabric/authoring\n *\n * @example\n * ```typescript\n * import { defineBlock, createCustomBlockRegistry } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * const MyBlock = defineBlock({\n * type: 'my-block',\n * name: 'My Block',\n * schema: z.object({ title: z.string() }),\n * defaultProps: { title: 'Default' },\n * component: ({ props }) => <div>{props.title}</div>\n * })\n *\n * const registry = createCustomBlockRegistry()\n * registry.register(MyBlock)\n * ```\n */\n\n// Re-export runtime (which includes core and contracts)\nexport * from '@pwfabric/runtime'\n\n// ============================================================\n// TYPES\n// ============================================================\n\nexport type {\n // Custom block types\n CustomBlockOptions,\n SchemaProps,\n CustomBlockComponentProps,\n CustomBlockComponent,\n CustomBlockContext,\n CustomBlockDefinition,\n\n // Factory types\n FactoryConfig,\n BlockSpec,\n FactoryOptions,\n FactoryDefinition,\n FactoryValidationResult,\n FactoryValidationError,\n\n // Registry types\n ICustomBlockRegistry,\n\n // Validation types\n BlockValidationResult,\n BlockValidationError,\n PropsValidationResult,\n\n // Utility types\n InferBlockProps,\n InferFactoryConfig,\n InferSchemaProps,\n} from './types.js'\n\n// ============================================================\n// DEFINE BLOCK API\n// ============================================================\n\nexport { defineBlock, isCustomBlockDefinition, createBlockInstance } from './define-block.js'\n// single-source field-severity gate for authoring/install.\nexport { validateBlockDefinition } from './define-block.js'\nexport type { BlockDefinitionIssue } from './define-block.js'\n\n// ============================================================\n// DEFINE FACTORY API\n// ============================================================\n\nexport {\n defineFactory,\n isFactoryDefinition,\n composeFactoryOutputs,\n wrapInContainer,\n resetIdCounter,\n} from './define-factory.js'\n\n// ============================================================\n// DEFINE CAPABILITY API\n// ============================================================\n\nexport { CapabilityBuilder, defineCapability } from '@pwfabric/contracts'\n\n// ============================================================\n// REGISTRY API\n// ============================================================\n\nexport {\n CustomBlockRegistry,\n createCustomBlockRegistry,\n getCustomBlockRegistry,\n resetGlobalRegistry,\n mergeRegistries,\n mergeWithBaseComponents,\n} from './registry.js'\n\n// ============================================================\n// VALIDATION API\n// ============================================================\n\nexport {\n validateProps,\n validatePropsWithDefaults,\n validateBlock,\n validateBlockTree,\n createBlockValidator,\n createPropsValidator,\n formatValidationErrors,\n hasErrors,\n isBlock,\n isBlockArray,\n} from './validation.js'\n"]}
|