@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,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CustomBlockRegistry
|
|
3
|
+
*
|
|
4
|
+
* Registry for managing custom block definitions.
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/authoring/registry
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createCustomBlockRegistry, defineBlock } from '@pwfabric/authoring'
|
|
11
|
+
* import { z } from 'zod'
|
|
12
|
+
*
|
|
13
|
+
* const registry = createCustomBlockRegistry()
|
|
14
|
+
*
|
|
15
|
+
* const MyBlock = defineBlock({
|
|
16
|
+
* type: 'my-block',
|
|
17
|
+
* name: 'My Block',
|
|
18
|
+
* schema: z.object({ title: z.string() }),
|
|
19
|
+
* defaultProps: { title: 'Default' },
|
|
20
|
+
* component: ({ props }) => <div>{props.title}</div>
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* registry.register(MyBlock)
|
|
24
|
+
*
|
|
25
|
+
* // Use with SurfaceRenderer
|
|
26
|
+
* <SurfaceRenderer components={registry.getComponents()} />
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
import type { BlockCategory } from '@pwfabric/core';
|
|
30
|
+
import type { BlockComponentRegistry } from '@pwfabric/runtime';
|
|
31
|
+
import type { CustomBlockDefinition, ICustomBlockRegistry } from './types.js';
|
|
32
|
+
/**
|
|
33
|
+
* CustomBlockRegistry
|
|
34
|
+
*
|
|
35
|
+
* Manages registration and retrieval of custom block definitions.
|
|
36
|
+
* Provides a bridge between custom block definitions and the
|
|
37
|
+
* SurfaceRenderer component registry.
|
|
38
|
+
*/
|
|
39
|
+
export declare class CustomBlockRegistry implements ICustomBlockRegistry {
|
|
40
|
+
private readonly definitions;
|
|
41
|
+
/**
|
|
42
|
+
* Register a custom block definition
|
|
43
|
+
*
|
|
44
|
+
* @param definition - The custom block definition to register
|
|
45
|
+
* @throws Error if block type already registered
|
|
46
|
+
*/
|
|
47
|
+
register<TProps extends Record<string, unknown>>(definition: CustomBlockDefinition<TProps>): void;
|
|
48
|
+
/**
|
|
49
|
+
* Register multiple custom block definitions
|
|
50
|
+
*
|
|
51
|
+
* @param definitions - Array of custom block definitions
|
|
52
|
+
*/
|
|
53
|
+
registerMany(definitions: readonly CustomBlockDefinition[]): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get a custom block definition by type
|
|
56
|
+
*
|
|
57
|
+
* @param type - Block type identifier
|
|
58
|
+
* @returns The block definition or undefined
|
|
59
|
+
*/
|
|
60
|
+
get(type: string): CustomBlockDefinition | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Check if a block type is registered
|
|
63
|
+
*
|
|
64
|
+
* @param type - Block type identifier
|
|
65
|
+
* @returns True if registered
|
|
66
|
+
*/
|
|
67
|
+
has(type: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Get all registered custom block definitions
|
|
70
|
+
*
|
|
71
|
+
* @returns Array of all block definitions
|
|
72
|
+
*/
|
|
73
|
+
list(): readonly CustomBlockDefinition[];
|
|
74
|
+
/**
|
|
75
|
+
* Get block definitions by category
|
|
76
|
+
*
|
|
77
|
+
* @param category - Block category
|
|
78
|
+
* @returns Array of block definitions in category
|
|
79
|
+
*/
|
|
80
|
+
getByCategory(category: BlockCategory): readonly CustomBlockDefinition[];
|
|
81
|
+
/**
|
|
82
|
+
* Get all registered block types
|
|
83
|
+
*
|
|
84
|
+
* @returns Array of block type identifiers
|
|
85
|
+
*/
|
|
86
|
+
types(): readonly string[];
|
|
87
|
+
/**
|
|
88
|
+
* Unregister a custom block
|
|
89
|
+
*
|
|
90
|
+
* @param type - Block type identifier
|
|
91
|
+
* @returns True if block was unregistered
|
|
92
|
+
*/
|
|
93
|
+
unregister(type: string): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Clear all registrations
|
|
96
|
+
*/
|
|
97
|
+
clear(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Get number of registered blocks
|
|
100
|
+
*/
|
|
101
|
+
get size(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Get a component registry for use with SurfaceRenderer
|
|
104
|
+
*
|
|
105
|
+
* This method creates a BlockComponentRegistry that can be passed
|
|
106
|
+
* to the SurfaceRenderer's `components` prop to enable rendering
|
|
107
|
+
* of custom blocks.
|
|
108
|
+
*
|
|
109
|
+
* @returns BlockComponentRegistry with all registered custom blocks
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
114
|
+
*
|
|
115
|
+
* const registry = createCustomBlockRegistry()
|
|
116
|
+
* registry.register(MyCustomBlock)
|
|
117
|
+
*
|
|
118
|
+
* // Pass to SurfaceRenderer
|
|
119
|
+
* <SurfaceRenderer
|
|
120
|
+
* surface={mySurface}
|
|
121
|
+
* components={registry.getComponents()}
|
|
122
|
+
* />
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
getComponents(): BlockComponentRegistry;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Create a new CustomBlockRegistry
|
|
129
|
+
*
|
|
130
|
+
* @returns A new CustomBlockRegistry instance
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const registry = createCustomBlockRegistry()
|
|
135
|
+
* registry.register(MyBlock)
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare function createCustomBlockRegistry(): CustomBlockRegistry;
|
|
139
|
+
export declare function getCustomBlockRegistry(): CustomBlockRegistry;
|
|
140
|
+
/**
|
|
141
|
+
* Reset the global registry
|
|
142
|
+
*
|
|
143
|
+
* Useful for testing or when you need to clear all registrations.
|
|
144
|
+
*/
|
|
145
|
+
export declare function resetGlobalRegistry(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Merge multiple custom block registries into one
|
|
148
|
+
*
|
|
149
|
+
* @param registries - Registries to merge
|
|
150
|
+
* @returns A new registry containing all blocks from input registries
|
|
151
|
+
* @throws Error if there are duplicate block types
|
|
152
|
+
*/
|
|
153
|
+
export declare function mergeRegistries(...registries: readonly CustomBlockRegistry[]): CustomBlockRegistry;
|
|
154
|
+
/**
|
|
155
|
+
* Merge a custom block registry with a base BlockComponentRegistry
|
|
156
|
+
*
|
|
157
|
+
* This is useful when you want to extend the default block components
|
|
158
|
+
* with custom blocks.
|
|
159
|
+
*
|
|
160
|
+
* @param baseComponents - The base component registry (e.g., defaultBlockComponents)
|
|
161
|
+
* @param customRegistry - The custom block registry
|
|
162
|
+
* @returns A merged BlockComponentRegistry
|
|
163
|
+
*/
|
|
164
|
+
export declare function mergeWithBaseComponents(baseComponents: BlockComponentRegistry, customRegistry: CustomBlockRegistry): BlockComponentRegistry;
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CustomBlockRegistry
|
|
3
|
+
*
|
|
4
|
+
* Registry for managing custom block definitions.
|
|
5
|
+
*
|
|
6
|
+
* @module @pwfabric/authoring/registry
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { createCustomBlockRegistry, defineBlock } from '@pwfabric/authoring'
|
|
11
|
+
* import { z } from 'zod'
|
|
12
|
+
*
|
|
13
|
+
* const registry = createCustomBlockRegistry()
|
|
14
|
+
*
|
|
15
|
+
* const MyBlock = defineBlock({
|
|
16
|
+
* type: 'my-block',
|
|
17
|
+
* name: 'My Block',
|
|
18
|
+
* schema: z.object({ title: z.string() }),
|
|
19
|
+
* defaultProps: { title: 'Default' },
|
|
20
|
+
* component: ({ props }) => <div>{props.title}</div>
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* registry.register(MyBlock)
|
|
24
|
+
*
|
|
25
|
+
* // Use with SurfaceRenderer
|
|
26
|
+
* <SurfaceRenderer components={registry.getComponents()} />
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
import { createElement } from 'react';
|
|
30
|
+
// ============================================================
|
|
31
|
+
// CUSTOM BLOCK REGISTRY
|
|
32
|
+
// ============================================================
|
|
33
|
+
/**
|
|
34
|
+
* CustomBlockRegistry
|
|
35
|
+
*
|
|
36
|
+
* Manages registration and retrieval of custom block definitions.
|
|
37
|
+
* Provides a bridge between custom block definitions and the
|
|
38
|
+
* SurfaceRenderer component registry.
|
|
39
|
+
*/
|
|
40
|
+
export class CustomBlockRegistry {
|
|
41
|
+
definitions = new Map();
|
|
42
|
+
/**
|
|
43
|
+
* Register a custom block definition
|
|
44
|
+
*
|
|
45
|
+
* @param definition - The custom block definition to register
|
|
46
|
+
* @throws Error if block type already registered
|
|
47
|
+
*/
|
|
48
|
+
register(definition) {
|
|
49
|
+
if (this.definitions.has(definition.type)) {
|
|
50
|
+
throw new Error(`Block type "${definition.type}" is already registered`);
|
|
51
|
+
}
|
|
52
|
+
this.definitions.set(definition.type, definition);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Register multiple custom block definitions
|
|
56
|
+
*
|
|
57
|
+
* @param definitions - Array of custom block definitions
|
|
58
|
+
*/
|
|
59
|
+
registerMany(definitions) {
|
|
60
|
+
for (const definition of definitions) {
|
|
61
|
+
this.register(definition);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get a custom block definition by type
|
|
66
|
+
*
|
|
67
|
+
* @param type - Block type identifier
|
|
68
|
+
* @returns The block definition or undefined
|
|
69
|
+
*/
|
|
70
|
+
get(type) {
|
|
71
|
+
return this.definitions.get(type);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a block type is registered
|
|
75
|
+
*
|
|
76
|
+
* @param type - Block type identifier
|
|
77
|
+
* @returns True if registered
|
|
78
|
+
*/
|
|
79
|
+
has(type) {
|
|
80
|
+
return this.definitions.has(type);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get all registered custom block definitions
|
|
84
|
+
*
|
|
85
|
+
* @returns Array of all block definitions
|
|
86
|
+
*/
|
|
87
|
+
list() {
|
|
88
|
+
return Array.from(this.definitions.values());
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get block definitions by category
|
|
92
|
+
*
|
|
93
|
+
* @param category - Block category
|
|
94
|
+
* @returns Array of block definitions in category
|
|
95
|
+
*/
|
|
96
|
+
getByCategory(category) {
|
|
97
|
+
return this.list().filter((def) => def.category === category);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get all registered block types
|
|
101
|
+
*
|
|
102
|
+
* @returns Array of block type identifiers
|
|
103
|
+
*/
|
|
104
|
+
types() {
|
|
105
|
+
return Array.from(this.definitions.keys());
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Unregister a custom block
|
|
109
|
+
*
|
|
110
|
+
* @param type - Block type identifier
|
|
111
|
+
* @returns True if block was unregistered
|
|
112
|
+
*/
|
|
113
|
+
unregister(type) {
|
|
114
|
+
return this.definitions.delete(type);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Clear all registrations
|
|
118
|
+
*/
|
|
119
|
+
clear() {
|
|
120
|
+
this.definitions.clear();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get number of registered blocks
|
|
124
|
+
*/
|
|
125
|
+
get size() {
|
|
126
|
+
return this.definitions.size;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a component registry for use with SurfaceRenderer
|
|
130
|
+
*
|
|
131
|
+
* This method creates a BlockComponentRegistry that can be passed
|
|
132
|
+
* to the SurfaceRenderer's `components` prop to enable rendering
|
|
133
|
+
* of custom blocks.
|
|
134
|
+
*
|
|
135
|
+
* @returns BlockComponentRegistry with all registered custom blocks
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
140
|
+
*
|
|
141
|
+
* const registry = createCustomBlockRegistry()
|
|
142
|
+
* registry.register(MyCustomBlock)
|
|
143
|
+
*
|
|
144
|
+
* // Pass to SurfaceRenderer
|
|
145
|
+
* <SurfaceRenderer
|
|
146
|
+
* surface={mySurface}
|
|
147
|
+
* components={registry.getComponents()}
|
|
148
|
+
* />
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
getComponents() {
|
|
152
|
+
const components = {};
|
|
153
|
+
for (const definition of this.definitions.values()) {
|
|
154
|
+
components[definition.type] = createBlockComponentWrapper(definition);
|
|
155
|
+
}
|
|
156
|
+
return components;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a wrapper component that adapts CustomBlockComponent to BlockComponent
|
|
161
|
+
*
|
|
162
|
+
* This bridges the gap between the SDK's CustomBlockComponent interface
|
|
163
|
+
* and the runtime's BlockComponent interface.
|
|
164
|
+
*/
|
|
165
|
+
/**
|
|
166
|
+
* Map runtime BlockContext to SDK CustomBlockContext
|
|
167
|
+
*
|
|
168
|
+
* Narrows the full runtime context to only what custom block authors need.
|
|
169
|
+
* Excludes: capabilities, imageAdapter, basePath (platform internals).
|
|
170
|
+
*/
|
|
171
|
+
function mapToCustomBlockContext(runtimeContext) {
|
|
172
|
+
if (!runtimeContext || typeof runtimeContext !== 'object')
|
|
173
|
+
return undefined;
|
|
174
|
+
const ctx = runtimeContext;
|
|
175
|
+
return {
|
|
176
|
+
theme: ctx.theme ?? 'light',
|
|
177
|
+
viewport: ctx.viewport ?? 'lg',
|
|
178
|
+
onEvent: typeof ctx.onEvent === 'function'
|
|
179
|
+
? (event) => {
|
|
180
|
+
;
|
|
181
|
+
ctx.onEvent(event);
|
|
182
|
+
}
|
|
183
|
+
: undefined,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function createBlockComponentWrapper(definition) {
|
|
187
|
+
return function WrappedComponent({ block, props: rawProps, children, context, }) {
|
|
188
|
+
// Validate and apply defaults using the schema
|
|
189
|
+
const result = definition.schema.safeParse(rawProps);
|
|
190
|
+
const validatedProps = result.success ? result.data : definition.defaultProps;
|
|
191
|
+
// Map runtime context to custom block context
|
|
192
|
+
const customContext = mapToCustomBlockContext(context);
|
|
193
|
+
// Create the custom block component props
|
|
194
|
+
const customProps = {
|
|
195
|
+
props: validatedProps,
|
|
196
|
+
blockId: block.id,
|
|
197
|
+
children,
|
|
198
|
+
context: customContext,
|
|
199
|
+
};
|
|
200
|
+
// Render using the custom component
|
|
201
|
+
return createElement(definition.component, customProps);
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// ============================================================
|
|
205
|
+
// FACTORY FUNCTIONS
|
|
206
|
+
// ============================================================
|
|
207
|
+
/**
|
|
208
|
+
* Create a new CustomBlockRegistry
|
|
209
|
+
*
|
|
210
|
+
* @returns A new CustomBlockRegistry instance
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* const registry = createCustomBlockRegistry()
|
|
215
|
+
* registry.register(MyBlock)
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function createCustomBlockRegistry() {
|
|
219
|
+
return new CustomBlockRegistry();
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get a singleton instance of CustomBlockRegistry
|
|
223
|
+
*
|
|
224
|
+
* Use this when you want a shared registry across your application.
|
|
225
|
+
* For isolated registries (e.g., in tests), use `createCustomBlockRegistry()`.
|
|
226
|
+
*/
|
|
227
|
+
let globalRegistry = null;
|
|
228
|
+
export function getCustomBlockRegistry() {
|
|
229
|
+
if (!globalRegistry) {
|
|
230
|
+
globalRegistry = new CustomBlockRegistry();
|
|
231
|
+
}
|
|
232
|
+
return globalRegistry;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Reset the global registry
|
|
236
|
+
*
|
|
237
|
+
* Useful for testing or when you need to clear all registrations.
|
|
238
|
+
*/
|
|
239
|
+
export function resetGlobalRegistry() {
|
|
240
|
+
globalRegistry = null;
|
|
241
|
+
}
|
|
242
|
+
// ============================================================
|
|
243
|
+
// MERGE UTILITIES
|
|
244
|
+
// ============================================================
|
|
245
|
+
/**
|
|
246
|
+
* Merge multiple custom block registries into one
|
|
247
|
+
*
|
|
248
|
+
* @param registries - Registries to merge
|
|
249
|
+
* @returns A new registry containing all blocks from input registries
|
|
250
|
+
* @throws Error if there are duplicate block types
|
|
251
|
+
*/
|
|
252
|
+
export function mergeRegistries(...registries) {
|
|
253
|
+
const merged = new CustomBlockRegistry();
|
|
254
|
+
for (const registry of registries) {
|
|
255
|
+
for (const definition of registry.list()) {
|
|
256
|
+
merged.register(definition);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return merged;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Merge a custom block registry with a base BlockComponentRegistry
|
|
263
|
+
*
|
|
264
|
+
* This is useful when you want to extend the default block components
|
|
265
|
+
* with custom blocks.
|
|
266
|
+
*
|
|
267
|
+
* @param baseComponents - The base component registry (e.g., defaultBlockComponents)
|
|
268
|
+
* @param customRegistry - The custom block registry
|
|
269
|
+
* @returns A merged BlockComponentRegistry
|
|
270
|
+
*/
|
|
271
|
+
export function mergeWithBaseComponents(baseComponents, customRegistry) {
|
|
272
|
+
return {
|
|
273
|
+
...baseComponents,
|
|
274
|
+
...customRegistry.getComponents(),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CustomBlockRegistry\n *\n * Registry for managing custom block definitions.\n *\n * @module @pwfabric/authoring/registry\n *\n * @example\n * ```typescript\n * import { createCustomBlockRegistry, defineBlock } from '@pwfabric/authoring'\n * import { z } from 'zod'\n *\n * const registry = createCustomBlockRegistry()\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 * registry.register(MyBlock)\n *\n * // Use with SurfaceRenderer\n * <SurfaceRenderer components={registry.getComponents()} />\n * ```\n */\n\nimport type { BlockCategory, Block } from '@pwfabric/core'\nimport type { BlockComponentRegistry } from '@pwfabric/runtime'\nimport type {\n CustomBlockDefinition,\n ICustomBlockRegistry,\n CustomBlockComponentProps,\n CustomBlockContext,\n} from './types.js'\nimport { createElement, type ReactNode } from 'react'\n\n// ============================================================\n// CUSTOM BLOCK REGISTRY\n// ============================================================\n\n/**\n * CustomBlockRegistry\n *\n * Manages registration and retrieval of custom block definitions.\n * Provides a bridge between custom block definitions and the\n * SurfaceRenderer component registry.\n */\nexport class CustomBlockRegistry implements ICustomBlockRegistry {\n private readonly definitions = new Map<string, CustomBlockDefinition>()\n\n /**\n * Register a custom block definition\n *\n * @param definition - The custom block definition to register\n * @throws Error if block type already registered\n */\n register<TProps extends Record<string, unknown>>(\n definition: CustomBlockDefinition<TProps>\n ): void {\n if (this.definitions.has(definition.type)) {\n throw new Error(`Block type \"${definition.type}\" is already registered`)\n }\n this.definitions.set(definition.type, definition as CustomBlockDefinition)\n }\n\n /**\n * Register multiple custom block definitions\n *\n * @param definitions - Array of custom block definitions\n */\n registerMany(definitions: readonly CustomBlockDefinition[]): void {\n for (const definition of definitions) {\n this.register(definition)\n }\n }\n\n /**\n * Get a custom block definition by type\n *\n * @param type - Block type identifier\n * @returns The block definition or undefined\n */\n get(type: string): CustomBlockDefinition | undefined {\n return this.definitions.get(type)\n }\n\n /**\n * Check if a block type is registered\n *\n * @param type - Block type identifier\n * @returns True if registered\n */\n has(type: string): boolean {\n return this.definitions.has(type)\n }\n\n /**\n * Get all registered custom block definitions\n *\n * @returns Array of all block definitions\n */\n list(): readonly CustomBlockDefinition[] {\n return Array.from(this.definitions.values())\n }\n\n /**\n * Get block definitions by category\n *\n * @param category - Block category\n * @returns Array of block definitions in category\n */\n getByCategory(category: BlockCategory): readonly CustomBlockDefinition[] {\n return this.list().filter((def) => def.category === category)\n }\n\n /**\n * Get all registered block types\n *\n * @returns Array of block type identifiers\n */\n types(): readonly string[] {\n return Array.from(this.definitions.keys())\n }\n\n /**\n * Unregister a custom block\n *\n * @param type - Block type identifier\n * @returns True if block was unregistered\n */\n unregister(type: string): boolean {\n return this.definitions.delete(type)\n }\n\n /**\n * Clear all registrations\n */\n clear(): void {\n this.definitions.clear()\n }\n\n /**\n * Get number of registered blocks\n */\n get size(): number {\n return this.definitions.size\n }\n\n /**\n * Get a component registry for use with SurfaceRenderer\n *\n * This method creates a BlockComponentRegistry that can be passed\n * to the SurfaceRenderer's `components` prop to enable rendering\n * of custom blocks.\n *\n * @returns BlockComponentRegistry with all registered custom blocks\n *\n * @example\n * ```typescript\n * import { SurfaceRenderer } from '@pwfabric/runtime'\n *\n * const registry = createCustomBlockRegistry()\n * registry.register(MyCustomBlock)\n *\n * // Pass to SurfaceRenderer\n * <SurfaceRenderer\n * surface={mySurface}\n * components={registry.getComponents()}\n * />\n * ```\n */\n getComponents(): BlockComponentRegistry {\n const components: BlockComponentRegistry = {}\n\n for (const definition of this.definitions.values()) {\n components[definition.type] = createBlockComponentWrapper(definition)\n }\n\n return components\n }\n}\n\n/**\n * Create a wrapper component that adapts CustomBlockComponent to BlockComponent\n *\n * This bridges the gap between the SDK's CustomBlockComponent interface\n * and the runtime's BlockComponent interface.\n */\n/**\n * Map runtime BlockContext to SDK CustomBlockContext\n *\n * Narrows the full runtime context to only what custom block authors need.\n * Excludes: capabilities, imageAdapter, basePath (platform internals).\n */\nfunction mapToCustomBlockContext(runtimeContext: unknown): CustomBlockContext | undefined {\n if (!runtimeContext || typeof runtimeContext !== 'object') return undefined\n\n const ctx = runtimeContext as Record<string, unknown>\n return {\n theme: (ctx.theme as 'light' | 'dark') ?? 'light',\n viewport: (ctx.viewport as string) ?? 'lg',\n onEvent:\n typeof ctx.onEvent === 'function'\n ? (event: { type: string; blockId?: string; payload?: unknown }) => {\n ;(ctx.onEvent as (e: unknown) => void)(event)\n }\n : undefined,\n }\n}\n\nfunction createBlockComponentWrapper(\n definition: CustomBlockDefinition\n): (props: {\n block: Block\n props: Record<string, unknown>\n children?: ReactNode\n context: unknown\n}) => ReactNode {\n return function WrappedComponent({\n block,\n props: rawProps,\n children,\n context,\n }: {\n block: Block\n props: Record<string, unknown>\n children?: ReactNode\n context: unknown\n }): ReactNode {\n // Validate and apply defaults using the schema\n const result = definition.schema.safeParse(rawProps)\n const validatedProps = result.success ? result.data : definition.defaultProps\n\n // Map runtime context to custom block context\n const customContext = mapToCustomBlockContext(context)\n\n // Create the custom block component props\n const customProps: CustomBlockComponentProps = {\n props: validatedProps,\n blockId: block.id,\n children,\n context: customContext,\n }\n\n // Render using the custom component\n return createElement(\n definition.component as React.ComponentType<CustomBlockComponentProps>,\n customProps\n )\n }\n}\n\n// ============================================================\n// FACTORY FUNCTIONS\n// ============================================================\n\n/**\n * Create a new CustomBlockRegistry\n *\n * @returns A new CustomBlockRegistry instance\n *\n * @example\n * ```typescript\n * const registry = createCustomBlockRegistry()\n * registry.register(MyBlock)\n * ```\n */\nexport function createCustomBlockRegistry(): CustomBlockRegistry {\n return new CustomBlockRegistry()\n}\n\n/**\n * Get a singleton instance of CustomBlockRegistry\n *\n * Use this when you want a shared registry across your application.\n * For isolated registries (e.g., in tests), use `createCustomBlockRegistry()`.\n */\nlet globalRegistry: CustomBlockRegistry | null = null\n\nexport function getCustomBlockRegistry(): CustomBlockRegistry {\n if (!globalRegistry) {\n globalRegistry = new CustomBlockRegistry()\n }\n return globalRegistry\n}\n\n/**\n * Reset the global registry\n *\n * Useful for testing or when you need to clear all registrations.\n */\nexport function resetGlobalRegistry(): void {\n globalRegistry = null\n}\n\n// ============================================================\n// MERGE UTILITIES\n// ============================================================\n\n/**\n * Merge multiple custom block registries into one\n *\n * @param registries - Registries to merge\n * @returns A new registry containing all blocks from input registries\n * @throws Error if there are duplicate block types\n */\nexport function mergeRegistries(\n ...registries: readonly CustomBlockRegistry[]\n): CustomBlockRegistry {\n const merged = new CustomBlockRegistry()\n\n for (const registry of registries) {\n for (const definition of registry.list()) {\n merged.register(definition)\n }\n }\n\n return merged\n}\n\n/**\n * Merge a custom block registry with a base BlockComponentRegistry\n *\n * This is useful when you want to extend the default block components\n * with custom blocks.\n *\n * @param baseComponents - The base component registry (e.g., defaultBlockComponents)\n * @param customRegistry - The custom block registry\n * @returns A merged BlockComponentRegistry\n */\nexport function mergeWithBaseComponents(\n baseComponents: BlockComponentRegistry,\n customRegistry: CustomBlockRegistry\n): BlockComponentRegistry {\n return {\n ...baseComponents,\n ...customRegistry.getComponents(),\n }\n}\n"]}
|