@jfdevelops/react-layout 0.24.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-config/create-defined-resource-layout.cjs +72 -0
- package/dist/create-config/create-defined-resource-layout.cjs.map +1 -0
- package/dist/create-config/create-defined-resource-layout.d.cts +88 -0
- package/dist/create-config/create-defined-resource-layout.d.cts.map +1 -0
- package/dist/create-config/create-defined-resource-layout.d.mts +88 -0
- package/dist/create-config/create-defined-resource-layout.d.mts.map +1 -0
- package/dist/create-config/create-defined-resource-layout.mjs +72 -0
- package/dist/create-config/create-defined-resource-layout.mjs.map +1 -0
- package/dist/create-config/create-resource-links.cjs.map +1 -1
- package/dist/create-config/create-resource-links.d.cts +1 -1
- package/dist/create-config/create-resource-links.d.cts.map +1 -1
- package/dist/create-config/create-resource-links.d.mts +1 -1
- package/dist/create-config/create-resource-links.d.mts.map +1 -1
- package/dist/create-config/create-resource-links.mjs.map +1 -1
- package/dist/create-config/define-layout.cjs +199 -9
- package/dist/create-config/define-layout.cjs.map +1 -1
- package/dist/create-config/define-layout.d.cts +172 -29
- package/dist/create-config/define-layout.d.cts.map +1 -1
- package/dist/create-config/define-layout.d.mts +172 -29
- package/dist/create-config/define-layout.d.mts.map +1 -1
- package/dist/create-config/define-layout.mjs +202 -13
- package/dist/create-config/define-layout.mjs.map +1 -1
- package/dist/create-config/for-resources/create-for-resources.cjs +7 -0
- package/dist/create-config/for-resources/create-for-resources.cjs.map +1 -1
- package/dist/create-config/for-resources/create-for-resources.mjs +7 -0
- package/dist/create-config/for-resources/create-for-resources.mjs.map +1 -1
- package/dist/create-config/get-component.cjs +22 -1
- package/dist/create-config/get-component.cjs.map +1 -1
- package/dist/create-config/get-component.d.cts +25 -3
- package/dist/create-config/get-component.d.cts.map +1 -1
- package/dist/create-config/get-component.d.mts +25 -3
- package/dist/create-config/get-component.d.mts.map +1 -1
- package/dist/create-config/get-component.mjs +22 -1
- package/dist/create-config/get-component.mjs.map +1 -1
- package/dist/create-config/index.d.cts +5 -2
- package/dist/create-config/index.d.mts +5 -2
- package/dist/create-config/lazy-resource-layout.cjs +111 -0
- package/dist/create-config/lazy-resource-layout.cjs.map +1 -0
- package/dist/create-config/lazy-resource-layout.d.cts +51 -0
- package/dist/create-config/lazy-resource-layout.d.cts.map +1 -0
- package/dist/create-config/lazy-resource-layout.d.mts +51 -0
- package/dist/create-config/lazy-resource-layout.d.mts.map +1 -0
- package/dist/create-config/lazy-resource-layout.mjs +111 -0
- package/dist/create-config/lazy-resource-layout.mjs.map +1 -0
- package/dist/create-config/render-state.cjs +62 -0
- package/dist/create-config/render-state.cjs.map +1 -0
- package/dist/create-config/render-state.d.cts +121 -0
- package/dist/create-config/render-state.d.cts.map +1 -0
- package/dist/create-config/render-state.d.mts +121 -0
- package/dist/create-config/render-state.d.mts.map +1 -0
- package/dist/create-config/render-state.mjs +55 -0
- package/dist/create-config/render-state.mjs.map +1 -0
- package/dist/errors/resource-state-error.cjs +29 -0
- package/dist/errors/resource-state-error.cjs.map +1 -0
- package/dist/errors/resource-state-error.d.cts +19 -0
- package/dist/errors/resource-state-error.d.cts.map +1 -0
- package/dist/errors/resource-state-error.d.mts +19 -0
- package/dist/errors/resource-state-error.d.mts.map +1 -0
- package/dist/errors/resource-state-error.mjs +27 -0
- package/dist/errors/resource-state-error.mjs.map +1 -0
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +9 -5
- package/dist/index.d.mts +9 -5
- package/dist/index.mjs +4 -2
- package/dist/props.d.cts +6 -1
- package/dist/props.d.cts.map +1 -1
- package/dist/props.d.mts +6 -1
- package/dist/props.d.mts.map +1 -1
- package/dist/resource.cjs +17 -0
- package/dist/resource.cjs.map +1 -1
- package/dist/resource.d.cts +33 -1
- package/dist/resource.d.cts.map +1 -1
- package/dist/resource.d.mts +33 -1
- package/dist/resource.d.mts.map +1 -1
- package/dist/resource.mjs +17 -1
- package/dist/resource.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -80,7 +80,7 @@ function createGetComponent(config) {
|
|
|
80
80
|
};
|
|
81
81
|
return Object.assign(getComponent, { forResource });
|
|
82
82
|
}
|
|
83
|
-
function
|
|
83
|
+
function createResourceConfigImpl(config) {
|
|
84
84
|
const configRecord = config;
|
|
85
85
|
if (collectConfigResources(configRecord).length === 0) throw new InvalidConfigError({
|
|
86
86
|
reason: "A resource config must configure at least one resource",
|
|
@@ -95,6 +95,27 @@ function createResourceConfig(config) {
|
|
|
95
95
|
subResources: collectAllSubResourceKeys(configRecord)
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
+
function createResourceConfigForResource(resource, config) {
|
|
99
|
+
return { [resource]: config };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Creates a validated resource configuration. Use `.forResource()` to build
|
|
103
|
+
* independently declared, type-safe fragments that can be spread into the
|
|
104
|
+
* complete configuration object.
|
|
105
|
+
*
|
|
106
|
+
* @param config - Resource entries to validate and expose through lookup helpers.
|
|
107
|
+
* @returns The original config plus typed component and path lookup helpers.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* const users = createResourceConfig.forResource('users', {
|
|
112
|
+
* component: <Users />,
|
|
113
|
+
* });
|
|
114
|
+
*
|
|
115
|
+
* const config = createResourceConfig({ ...users });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
const createResourceConfig = Object.assign(createResourceConfigImpl, { forResource: createResourceConfigForResource });
|
|
98
119
|
|
|
99
120
|
//#endregion
|
|
100
121
|
export { createResourceConfig };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-component.mjs","names":[],"sources":["../../src/create-config/get-component.ts"],"sourcesContent":["import type { JSX } from 'react';\nimport {\n InvalidComponentError,\n InvalidConfigError,\n InvalidPathError,\n InvalidResourceError,\n InvalidSubResourceError,\n} from '../errors';\nimport type {\n LayoutResourceKey,\n ResourceDefinition,\n ResourceDefinitionForKey,\n SubResourceDefinitionsFor,\n SubResourceParamForResource,\n} from '../resource';\nimport {\n createForPaths,\n createGetComponentForPath,\n type CreateGetComponentForPath,\n type ForPaths,\n} from './for-paths';\nimport {\n createIsSubResourceKey,\n type IsSubResourceKey,\n} from './is-sub-resource-key';\nimport {\n readComponentKeys,\n readSubResourceKeys,\n} from './component-keys';\nimport {\n collectAllSubResourceKeys,\n collectConfigResources,\n} from './paths';\nimport type {\n ConfigSubResourceKeys,\n ParameterizedResourcePath,\n} from './paths';\nimport type {\n BaseResourceConfigComponents,\n ResourceConfigComponentKey,\n ResourceConfigComponents,\n ResourceConfigEntry,\n ResourceConfigMap,\n ResourceConfigInput,\n SharedResourceConfigOptions,\n} from './types';\n\ntype ResourceHasLayoutSubResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n> = [SubResourceDefinitionsFor<Resources, Resource>] extends [readonly []]\n ? false\n : true;\n\ntype SubResourceOptionForGenerics<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined,\n> = [SubResource] extends [undefined]\n ? ResourceHasLayoutSubResources<Resources, Resource> extends true\n ? {\n subResource?: SubResourceParamForResource<Resources, Resource>;\n }\n : {}\n : ResourceHasLayoutSubResources<Resources, Resource> extends true\n ? { subResource?: SubResource }\n : {};\n\n/** Options for one layout resource passed to {@link GetComponent}. */\nexport type GetComponentOptionsForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = SubResourceOptionForGenerics<Resources, Resource, SubResource> & {\n resource: Resource;\n /**\n * The component to get.\n *\n * @default 'component'\n */\n component?: ResourceConfigComponentKey;\n};\n\n/** Union of valid `getComponent` option shapes for each declared layout resource. */\nexport type GetComponentOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource\n ? Resource extends LayoutResourceKey<Resources>\n ? GetComponentOptionsForResource<Resources, Resource>\n : never\n : never;\n\ntype ComponentSlotPathKey = keyof BaseResourceConfigComponents;\n\n/** `detail.errorComponent`, `new.component`, … — component slots inside a shared branch. */\ntype SharedBranchPath = {\n [Branch in keyof SharedResourceConfigOptions]: `${Branch}.${ComponentSlotPathKey}`;\n}[keyof SharedResourceConfigOptions];\n\ntype NestedSubResourceDefinitions<Def extends ResourceDefinition> = Def extends {\n subResources: infer Nested extends ReadonlyArray<ResourceDefinition>;\n}\n ? Nested\n : readonly [];\n\ntype SubResourceEntryPaths<\n SubDefs extends ReadonlyArray<ResourceDefinition>,\n DepthAcc extends readonly unknown[],\n> =\n LayoutResourceKey<SubDefs> extends infer Key extends string\n ? Key extends LayoutResourceKey<SubDefs>\n ?\n | Key\n | `${Key}.${ResourceConfigEntryPath<\n NestedSubResourceDefinitions<\n ResourceDefinitionForKey<SubDefs, Key>\n >,\n readonly [...DepthAcc, unknown]\n >}`\n : never\n : never;\n\n/** Dot-separated keys below one config node: component slots and nested sub-resources. */\ntype ResourceConfigEntryPath<\n SubDefs extends ReadonlyArray<ResourceDefinition>,\n DepthAcc extends readonly unknown[] = readonly [],\n> =\n | ComponentSlotPathKey\n | SharedBranchPath\n | ([SubDefs] extends [readonly []]\n ? never\n : DepthAcc['length'] extends 6\n ? never\n : SubResourceEntryPaths<SubDefs, DepthAcc>);\n\n/**\n * Deep config paths accepted by {@link GetComponent}, e.g.\n * `'templates.deleted.expired.errorComponent'`. Sub-resource segments come from the\n * layout tree, so only declared sub-resources are allowed at each depth.\n */\nexport type ResourceConfigPath<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource extends string\n ? Resource extends LayoutResourceKey<Resources>\n ?\n | Resource\n | `${Resource}.${ResourceConfigEntryPath<\n SubResourceDefinitionsFor<Resources, Resource>\n >}`\n : never\n : never;\n\n/** `subResource` bound by {@link GetComponentForResource}. */\nexport type SubResourceFromGetComponentBound<Bound> = Bound extends {\n subResource: infer SubResource;\n}\n ? SubResource\n : undefined;\n\n/** Options for {@link GetComponentForResource} — binds `resource` and optional `subResource`. */\nexport type GetComponentForResourceOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = {\n resource: Resource;\n} & SubResourceOptionForGenerics<Resources, Resource, SubResource>;\n\n/** Valid bind shapes for {@link GetComponentForResource}. */\nexport type GetComponentForResourceBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource\n ? Resource extends LayoutResourceKey<Resources>\n ? GetComponentForResourceOptions<Resources, Resource>\n : never\n : never;\n\ntype GetComponentForResourceOptionsForBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound extends { resource: LayoutResourceKey<Resources> },\n> = GetComponentForResourceOptions<\n Resources,\n Bound['resource'],\n SubResourceFromGetComponentBound<Bound>\n>;\n\n/**\n * Rejects bind objects with keys (e.g. `subResource`) that are invalid for the\n * given `resource`. Union assignability alone is too permissive for excess keys.\n */\nexport type ValidateForResourceBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound extends { resource: LayoutResourceKey<Resources> },\n> = Bound extends GetComponentForResourceOptionsForBound<Resources, Bound>\n ? keyof Bound extends keyof GetComponentForResourceOptionsForBound<\n Resources,\n Bound\n >\n ? Bound\n : never\n : never;\n\n/** `resource` inferred from a {@link GetComponentForResource} bind argument. */\nexport type ResourceFromGetComponentBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound,\n> = Bound extends {\n resource: infer Resource extends LayoutResourceKey<Resources>;\n}\n ? Resource\n : never;\n\n/** Reusable getter returned by {@link GetComponentForResource} (only `component` remains). */\nexport type GetComponentAtBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = (\n options?: Pick<\n GetComponentOptionsForResource<Resources, Resource, SubResource>,\n 'component'\n >,\n) => JSX.Element;\n\nexport type GetComponentForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = <\n const Bound extends {\n resource: LayoutResourceKey<Resources>;\n },\n>(\n bound: ValidateForResourceBound<Resources, Bound>,\n) => GetComponentAtBound<\n Resources,\n ResourceFromGetComponentBound<Resources, Bound>,\n SubResourceFromGetComponentBound<Bound>\n>;\n\nexport interface GetComponent<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> {\n /** Looks up a component by resource / optional sub-resource / optional component slot. */\n <const Options extends GetComponentOptions<Resources>>(\n options: Options,\n ): JSX.Element;\n /**\n * Looks up a component by a deep config path.\n * A path ending on a branch (e.g. `'templates.deleted.expired'`) returns that branch's\n * `component`; a path ending on a slot returns that slot.\n */\n <const Path extends ResourceConfigPath<Resources>>(path: Path): JSX.Element;\n forResource: GetComponentForResource<Resources>;\n}\n\nexport interface CreatedResourceConfig<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Config extends ResourceConfigMap<Resources> = ResourceConfigMap<Resources>,\n> {\n config: Config;\n getComponent: GetComponent<Resources>;\n /**\n * Creates a reusable getter bound to one path. Variables the path leaves open become\n * the getter's arguments.\n *\n * @example\n * ```ts\n * const getDetail = createGetComponent('$resource.detail.$component');\n *\n * getDetail({ resource: 'templates', component: 'errorComponent' });\n * ```\n */\n createGetComponent: CreateGetComponentForPath<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /**\n * Builds a component for one or more paths. Declare which params the component takes,\n * then render it.\n *\n * @example\n * ```ts\n * const ResourceDetail = forPaths('$resource.detail.$component')\n * .addPathParams()\n * .render(({ params, getComponentForPath }) =>\n * getComponentForPath('$resource.detail.$component')(params),\n * );\n * ```\n */\n forPaths: ForPaths<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /** Narrows an unknown value to a sub-resource key configured for a path or resource. */\n isSubResourceKey: IsSubResourceKey<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /** Every sub-resource key configured in this config, at any depth. */\n subResources: ReadonlyArray<ConfigSubResourceKeys<Resources, Config>>;\n}\n\ntype ComponentTypesFromEntry<Entry> = Entry extends JSX.Element\n ? never\n : Entry extends object\n ?\n | Extract<keyof Entry, ResourceConfigComponentKey>\n | {\n [Key in keyof Entry]: NonNullable<Entry[Key]> extends JSX.Element\n ? never\n : ComponentTypesFromEntry<NonNullable<Entry[Key]>>;\n }[keyof Entry]\n : never;\n\n/**\n * Component selector keys actually defined anywhere in a created resource config.\n *\n * @example\n * ```ts\n * ComponentTypes<typeof resourceConfig>\n * // ^? may resolve to:`'component' | 'errorComponent' | 'detail'`.\n * ```\n */\nexport type ComponentTypes<Created> = Created extends {\n config: infer Config extends object;\n}\n ? {\n [Resource in keyof Config]: ComponentTypesFromEntry<\n NonNullable<Config[Resource]>\n >;\n }[keyof Config]\n : never;\n\nexport type CreateResourceConfigFn<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = <const Config extends ResourceConfigMap<Resources>>(\n config: ResourceConfigInput<Resources, Config>,\n) => CreatedResourceConfig<Resources, Config>;\n\ntype SubResourceParam =\n | string\n | {\n value: string;\n subResource: SubResourceParam;\n };\n\nfunction isResourceConfigEntry(value: unknown): value is ResourceConfigEntry {\n return typeof value === 'object' && value !== null;\n}\n\nfunction resolveResourceConfigEntry(\n entry: ResourceConfigEntry,\n subResource: SubResourceParam,\n): ResourceConfigEntry {\n const key = typeof subResource === 'string' ? subResource : subResource.value;\n const next = entry[key];\n\n if (!isResourceConfigEntry(next)) {\n throw new InvalidSubResourceError({\n subResource: key,\n validSubResources: readSubResourceKeys(entry),\n });\n }\n\n if (typeof subResource === 'string') {\n return next;\n }\n\n return resolveResourceConfigEntry(next, subResource.subResource);\n}\n\nfunction readResourceConfigComponent(\n entry: ResourceConfigComponents,\n componentKey: ResourceConfigComponentKey,\n): JSX.Element {\n if (componentKey === 'new' || componentKey === 'detail') {\n const branch = entry[componentKey];\n\n if (!branch?.component) {\n throw new InvalidComponentError({\n component: `${componentKey}.component`,\n validComponents: readComponentKeys(entry),\n });\n }\n\n return branch.component;\n }\n\n const value = entry[componentKey];\n\n if (!value) {\n throw new InvalidComponentError({\n component: componentKey,\n validComponents: readComponentKeys(entry),\n });\n }\n\n return value;\n}\n\nfunction getComponentFromOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(\n config: ResourceConfigMap<Resources>,\n options: GetComponentOptions<Resources>,\n): JSX.Element {\n const { resource, component: componentKey = 'component' } = options;\n const resourceEntry = config[resource];\n\n if (!resourceEntry) {\n throw new InvalidResourceError({\n resource,\n validResources: collectConfigResources(config as Record<string, unknown>),\n });\n }\n\n let entry = resourceEntry as ResourceConfigEntry;\n\n if ('subResource' in options && options.subResource !== undefined) {\n entry = resolveResourceConfigEntry(entry, options.subResource);\n }\n\n return readResourceConfigComponent(entry, componentKey);\n}\n\nfunction getComponentFromPath<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(config: ResourceConfigMap<Resources>, path: string): JSX.Element {\n let cursor: unknown = config;\n\n for (const segment of path.split('.')) {\n if (!isResourceConfigEntry(cursor)) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n const next: unknown = (cursor as Record<string, unknown>)[segment];\n\n if (next === undefined || next === null) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not configured`,\n });\n }\n\n cursor = next;\n }\n\n if (isResourceConfigEntry(cursor) && 'component' in cursor) {\n return readResourceConfigComponent(cursor, 'component');\n }\n\n return cursor as JSX.Element;\n}\n\nexport function createGetComponent<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(config: ResourceConfigMap<Resources>): GetComponent<Resources> {\n function getComponent(options: GetComponentOptions<Resources>): JSX.Element;\n function getComponent(path: ResourceConfigPath<Resources>): JSX.Element;\n function getComponent(\n options: GetComponentOptions<Resources> | ResourceConfigPath<Resources>,\n ): JSX.Element {\n if (typeof options === 'string') {\n return getComponentFromPath(config, options);\n }\n\n return getComponentFromOptions(config, options);\n }\n\n const forResource: GetComponentForResource<Resources> = (bound) => {\n return (options) =>\n getComponentFromOptions(config, {\n ...bound,\n ...options,\n } as unknown as GetComponentOptions<Resources>);\n };\n\n return Object.assign(getComponent, { forResource });\n}\n\nexport function createResourceConfig<\n Resources extends ReadonlyArray<ResourceDefinition>,\n const Config extends ResourceConfigMap<Resources>,\n>(\n config: ResourceConfigInput<Resources, Config>,\n): CreatedResourceConfig<Resources, Config> {\n const configRecord = config as Record<string, unknown>;\n\n if (collectConfigResources(configRecord).length === 0) {\n throw new InvalidConfigError({\n reason: 'A resource config must configure at least one resource',\n config,\n });\n }\n\n type Created = CreatedResourceConfig<Resources, Config>;\n\n return {\n config,\n getComponent: createGetComponent(config),\n createGetComponent: ((path: string) =>\n createGetComponentForPath(\n configRecord,\n path,\n )) as unknown as Created['createGetComponent'],\n forPaths: createForPaths(configRecord) as unknown as Created['forPaths'],\n isSubResourceKey: createIsSubResourceKey(\n configRecord,\n ) as unknown as Created['isSubResourceKey'],\n subResources: collectAllSubResourceKeys(\n configRecord,\n ) as unknown as Created['subResources'],\n };\n}\n"],"mappings":";;;;;;;;;;;;AAuWA,SAAS,sBAAsB,OAA8C;CAC3E,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,2BACP,OACA,aACqB;CACrB,MAAM,MAAM,OAAO,gBAAgB,WAAW,cAAc,YAAY;CACxE,MAAM,OAAO,MAAM;CAEnB,IAAI,CAAC,sBAAsB,IAAI,GAC7B,MAAM,IAAI,wBAAwB;EAChC,aAAa;EACb,mBAAmB,oBAAoB,KAAK;CAC9C,CAAC;CAGH,IAAI,OAAO,gBAAgB,UACzB,OAAO;CAGT,OAAO,2BAA2B,MAAM,YAAY,WAAW;AACjE;AAEA,SAAS,4BACP,OACA,cACa;CACb,IAAI,iBAAiB,SAAS,iBAAiB,UAAU;EACvD,MAAM,SAAS,MAAM;EAErB,IAAI,CAAC,QAAQ,WACX,MAAM,IAAI,sBAAsB;GAC9B,WAAW,GAAG,aAAa;GAC3B,iBAAiB,kBAAkB,KAAK;EAC1C,CAAC;EAGH,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,MAAM;CAEpB,IAAI,CAAC,OACH,MAAM,IAAI,sBAAsB;EAC9B,WAAW;EACX,iBAAiB,kBAAkB,KAAK;CAC1C,CAAC;CAGH,OAAO;AACT;AAEA,SAAS,wBAGP,QACA,SACa;CACb,MAAM,EAAE,UAAU,WAAW,eAAe,gBAAgB;CAC5D,MAAM,gBAAgB,OAAO;CAE7B,IAAI,CAAC,eACH,MAAM,IAAI,qBAAqB;EAC7B;EACA,gBAAgB,uBAAuB,MAAiC;CAC1E,CAAC;CAGH,IAAI,QAAQ;CAEZ,IAAI,iBAAiB,WAAW,QAAQ,gBAAgB,QACtD,QAAQ,2BAA2B,OAAO,QAAQ,WAAW;CAG/D,OAAO,4BAA4B,OAAO,YAAY;AACxD;AAEA,SAAS,qBAEP,QAAsC,MAA2B;CACjE,IAAI,SAAkB;CAEtB,KAAK,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;EACrC,IAAI,CAAC,sBAAsB,MAAM,GAC/B,MAAM,IAAI,iBAAiB;GACzB;GACA,QAAQ,IAAI,QAAQ;EACtB,CAAC;EAGH,MAAM,OAAiB,OAAmC;EAE1D,IAAI,SAAS,UAAa,SAAS,MACjC,MAAM,IAAI,iBAAiB;GACzB;GACA,QAAQ,IAAI,QAAQ;EACtB,CAAC;EAGH,SAAS;CACX;CAEA,IAAI,sBAAsB,MAAM,KAAK,eAAe,QAClD,OAAO,4BAA4B,QAAQ,WAAW;CAGxD,OAAO;AACT;AAEA,SAAgB,mBAEd,QAA+D;CAG/D,SAAS,aACP,SACa;EACb,IAAI,OAAO,YAAY,UACrB,OAAO,qBAAqB,QAAQ,OAAO;EAG7C,OAAO,wBAAwB,QAAQ,OAAO;CAChD;CAEA,MAAM,eAAmD,UAAU;EACjE,QAAQ,YACN,wBAAwB,QAAQ;GAC9B,GAAG;GACH,GAAG;EACL,CAA8C;CAClD;CAEA,OAAO,OAAO,OAAO,cAAc,EAAE,YAAY,CAAC;AACpD;AAEA,SAAgB,qBAId,QAC0C;CAC1C,MAAM,eAAe;CAErB,IAAI,uBAAuB,YAAY,EAAE,WAAW,GAClD,MAAM,IAAI,mBAAmB;EAC3B,QAAQ;EACR;CACF,CAAC;CAKH,OAAO;EACL;EACA,cAAc,mBAAmB,MAAM;EACvC,sBAAsB,SACpB,0BACE,cACA,IACF;EACF,UAAU,eAAe,YAAY;EACrC,kBAAkB,uBAChB,YACF;EACA,cAAc,0BACZ,YACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"get-component.mjs","names":[],"sources":["../../src/create-config/get-component.ts"],"sourcesContent":["import type { JSX } from 'react';\nimport {\n InvalidComponentError,\n InvalidConfigError,\n InvalidPathError,\n InvalidResourceError,\n InvalidSubResourceError,\n} from '../errors';\nimport type {\n LayoutResourceKey,\n ResourceDefinition,\n ResourceDefinitionForKey,\n SubResourceDefinitionsFor,\n SubResourceParamForResource,\n} from '../resource';\nimport {\n createForPaths,\n createGetComponentForPath,\n type CreateGetComponentForPath,\n type ForPaths,\n} from './for-paths';\nimport {\n createIsSubResourceKey,\n type IsSubResourceKey,\n} from './is-sub-resource-key';\nimport {\n readComponentKeys,\n readSubResourceKeys,\n} from './component-keys';\nimport {\n collectAllSubResourceKeys,\n collectConfigResources,\n} from './paths';\nimport type {\n ConfigSubResourceKeys,\n ParameterizedResourcePath,\n} from './paths';\nimport type {\n BaseResourceConfigComponents,\n ResourceConfig,\n ResourceConfigComponentKey,\n ResourceConfigComponents,\n ResourceConfigEntry,\n ResourceConfigMap,\n ResourceConfigInput,\n SharedResourceConfigOptions,\n} from './types';\n\ntype ResourceHasLayoutSubResources<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n> = [SubResourceDefinitionsFor<Resources, Resource>] extends [readonly []]\n ? false\n : true;\n\ntype SubResourceOptionForGenerics<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined,\n> = [SubResource] extends [undefined]\n ? ResourceHasLayoutSubResources<Resources, Resource> extends true\n ? {\n subResource?: SubResourceParamForResource<Resources, Resource>;\n }\n : {}\n : ResourceHasLayoutSubResources<Resources, Resource> extends true\n ? { subResource?: SubResource }\n : {};\n\n/** Options for one layout resource passed to {@link GetComponent}. */\nexport type GetComponentOptionsForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = SubResourceOptionForGenerics<Resources, Resource, SubResource> & {\n resource: Resource;\n /**\n * The component to get.\n *\n * @default 'component'\n */\n component?: ResourceConfigComponentKey;\n};\n\n/** Union of valid `getComponent` option shapes for each declared layout resource. */\nexport type GetComponentOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource\n ? Resource extends LayoutResourceKey<Resources>\n ? GetComponentOptionsForResource<Resources, Resource>\n : never\n : never;\n\ntype ComponentSlotPathKey = keyof BaseResourceConfigComponents;\n\n/** `detail.errorComponent`, `new.component`, … — component slots inside a shared branch. */\ntype SharedBranchPath = {\n [Branch in keyof SharedResourceConfigOptions]: `${Branch}.${ComponentSlotPathKey}`;\n}[keyof SharedResourceConfigOptions];\n\ntype NestedSubResourceDefinitions<Def extends ResourceDefinition> = Def extends {\n subResources: infer Nested extends ReadonlyArray<ResourceDefinition>;\n}\n ? Nested\n : readonly [];\n\ntype SubResourceEntryPaths<\n SubDefs extends ReadonlyArray<ResourceDefinition>,\n DepthAcc extends readonly unknown[],\n> =\n LayoutResourceKey<SubDefs> extends infer Key extends string\n ? Key extends LayoutResourceKey<SubDefs>\n ?\n | Key\n | `${Key}.${ResourceConfigEntryPath<\n NestedSubResourceDefinitions<\n ResourceDefinitionForKey<SubDefs, Key>\n >,\n readonly [...DepthAcc, unknown]\n >}`\n : never\n : never;\n\n/** Dot-separated keys below one config node: component slots and nested sub-resources. */\ntype ResourceConfigEntryPath<\n SubDefs extends ReadonlyArray<ResourceDefinition>,\n DepthAcc extends readonly unknown[] = readonly [],\n> =\n | ComponentSlotPathKey\n | SharedBranchPath\n | ([SubDefs] extends [readonly []]\n ? never\n : DepthAcc['length'] extends 6\n ? never\n : SubResourceEntryPaths<SubDefs, DepthAcc>);\n\n/**\n * Deep config paths accepted by {@link GetComponent}, e.g.\n * `'templates.deleted.expired.errorComponent'`. Sub-resource segments come from the\n * layout tree, so only declared sub-resources are allowed at each depth.\n */\nexport type ResourceConfigPath<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource extends string\n ? Resource extends LayoutResourceKey<Resources>\n ?\n | Resource\n | `${Resource}.${ResourceConfigEntryPath<\n SubResourceDefinitionsFor<Resources, Resource>\n >}`\n : never\n : never;\n\n/** `subResource` bound by {@link GetComponentForResource}. */\nexport type SubResourceFromGetComponentBound<Bound> = Bound extends {\n subResource: infer SubResource;\n}\n ? SubResource\n : undefined;\n\n/** Options for {@link GetComponentForResource} — binds `resource` and optional `subResource`. */\nexport type GetComponentForResourceOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = {\n resource: Resource;\n} & SubResourceOptionForGenerics<Resources, Resource, SubResource>;\n\n/** Valid bind shapes for {@link GetComponentForResource}. */\nexport type GetComponentForResourceBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> =\n LayoutResourceKey<Resources> extends infer Resource\n ? Resource extends LayoutResourceKey<Resources>\n ? GetComponentForResourceOptions<Resources, Resource>\n : never\n : never;\n\ntype GetComponentForResourceOptionsForBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound extends { resource: LayoutResourceKey<Resources> },\n> = GetComponentForResourceOptions<\n Resources,\n Bound['resource'],\n SubResourceFromGetComponentBound<Bound>\n>;\n\n/**\n * Rejects bind objects with keys (e.g. `subResource`) that are invalid for the\n * given `resource`. Union assignability alone is too permissive for excess keys.\n */\nexport type ValidateForResourceBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound extends { resource: LayoutResourceKey<Resources> },\n> = Bound extends GetComponentForResourceOptionsForBound<Resources, Bound>\n ? keyof Bound extends keyof GetComponentForResourceOptionsForBound<\n Resources,\n Bound\n >\n ? Bound\n : never\n : never;\n\n/** `resource` inferred from a {@link GetComponentForResource} bind argument. */\nexport type ResourceFromGetComponentBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Bound,\n> = Bound extends {\n resource: infer Resource extends LayoutResourceKey<Resources>;\n}\n ? Resource\n : never;\n\n/** Reusable getter returned by {@link GetComponentForResource} (only `component` remains). */\nexport type GetComponentAtBound<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Resource extends LayoutResourceKey<Resources>,\n SubResource extends\n | SubResourceParamForResource<Resources, Resource>\n | undefined = undefined,\n> = (\n options?: Pick<\n GetComponentOptionsForResource<Resources, Resource, SubResource>,\n 'component'\n >,\n) => JSX.Element;\n\nexport type GetComponentForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = <\n const Bound extends {\n resource: LayoutResourceKey<Resources>;\n },\n>(\n bound: ValidateForResourceBound<Resources, Bound>,\n) => GetComponentAtBound<\n Resources,\n ResourceFromGetComponentBound<Resources, Bound>,\n SubResourceFromGetComponentBound<Bound>\n>;\n\nexport interface GetComponent<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> {\n /** Looks up a component by resource / optional sub-resource / optional component slot. */\n <const Options extends GetComponentOptions<Resources>>(\n options: Options,\n ): JSX.Element;\n /**\n * Looks up a component by a deep config path.\n * A path ending on a branch (e.g. `'templates.deleted.expired'`) returns that branch's\n * `component`; a path ending on a slot returns that slot.\n */\n <const Path extends ResourceConfigPath<Resources>>(path: Path): JSX.Element;\n forResource: GetComponentForResource<Resources>;\n}\n\nexport interface CreatedResourceConfig<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Config extends ResourceConfigMap<Resources> = ResourceConfigMap<Resources>,\n> {\n config: Config;\n getComponent: GetComponent<Resources>;\n /**\n * Creates a reusable getter bound to one path. Variables the path leaves open become\n * the getter's arguments.\n *\n * @example\n * ```ts\n * const getDetail = createGetComponent('$resource.detail.$component');\n *\n * getDetail({ resource: 'templates', component: 'errorComponent' });\n * ```\n */\n createGetComponent: CreateGetComponentForPath<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /**\n * Builds a component for one or more paths. Declare which params the component takes,\n * then render it.\n *\n * @example\n * ```ts\n * const ResourceDetail = forPaths('$resource.detail.$component')\n * .addPathParams()\n * .render(({ params, getComponentForPath }) =>\n * getComponentForPath('$resource.detail.$component')(params),\n * );\n * ```\n */\n forPaths: ForPaths<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /** Narrows an unknown value to a sub-resource key configured for a path or resource. */\n isSubResourceKey: IsSubResourceKey<\n Resources,\n Config,\n ParameterizedResourcePath<Resources>\n >;\n /** Every sub-resource key configured in this config, at any depth. */\n subResources: ReadonlyArray<ConfigSubResourceKeys<Resources, Config>>;\n}\n\ntype ComponentTypesFromEntry<Entry> = Entry extends JSX.Element\n ? never\n : Entry extends object\n ?\n | Extract<keyof Entry, ResourceConfigComponentKey>\n | {\n [Key in keyof Entry]: NonNullable<Entry[Key]> extends JSX.Element\n ? never\n : ComponentTypesFromEntry<NonNullable<Entry[Key]>>;\n }[keyof Entry]\n : never;\n\n/**\n * Component selector keys actually defined anywhere in a created resource config.\n *\n * @example\n * ```ts\n * ComponentTypes<typeof resourceConfig>\n * // ^? may resolve to:`'component' | 'errorComponent' | 'detail'`.\n * ```\n */\nexport type ComponentTypes<Created> = Created extends {\n config: infer Config extends object;\n}\n ? {\n [Resource in keyof Config]: ComponentTypesFromEntry<\n NonNullable<Config[Resource]>\n >;\n }[keyof Config]\n : never;\n\n/**\n * One resource entry keyed for direct spreading into a resource config.\n * Preserves the precise component slots and sub-resource branches in `Config`.\n */\nexport type ResourceConfigFragment<\n Resource extends string,\n Config,\n> = { [Key in Resource]: Config };\n\n/**\n * Creates one keyed config fragment for spreading into a resource config.\n *\n * @param resource - A top-level resource supported by the layout definition.\n * @param config - Components and declared sub-resource branches for that resource.\n */\nexport type CreateResourceConfigForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = <\n const Resource extends LayoutResourceKey<Resources>,\n const Config extends ResourceConfig<Resources>[Resource],\n>(\n resource: Resource,\n config: Config,\n) => ResourceConfigFragment<Resource, Config>;\n\n/** Creates a validated resource config and exposes helpers for config fragments. */\nexport type CreateResourceConfigFn<\n Resources extends ReadonlyArray<ResourceDefinition>,\n> = {\n /**\n * Validates and indexes a complete configuration assembled from one or more\n * resource entries.\n *\n * @param config - At least one resource entry supported by the layout.\n */\n <const Config extends ResourceConfigMap<Resources>>(\n config: ResourceConfigInput<Resources, Config>,\n ): CreatedResourceConfig<Resources, Config>;\n /** Creates a typed fragment that can be spread into the complete config. */\n forResource: CreateResourceConfigForResource<Resources>;\n};\n\ntype SubResourceParam =\n | string\n | {\n value: string;\n subResource: SubResourceParam;\n };\n\nfunction isResourceConfigEntry(value: unknown): value is ResourceConfigEntry {\n return typeof value === 'object' && value !== null;\n}\n\nfunction resolveResourceConfigEntry(\n entry: ResourceConfigEntry,\n subResource: SubResourceParam,\n): ResourceConfigEntry {\n const key = typeof subResource === 'string' ? subResource : subResource.value;\n const next = entry[key];\n\n if (!isResourceConfigEntry(next)) {\n throw new InvalidSubResourceError({\n subResource: key,\n validSubResources: readSubResourceKeys(entry),\n });\n }\n\n if (typeof subResource === 'string') {\n return next;\n }\n\n return resolveResourceConfigEntry(next, subResource.subResource);\n}\n\nfunction readResourceConfigComponent(\n entry: ResourceConfigComponents,\n componentKey: ResourceConfigComponentKey,\n): JSX.Element {\n if (componentKey === 'new' || componentKey === 'detail') {\n const branch = entry[componentKey];\n\n if (!branch?.component) {\n throw new InvalidComponentError({\n component: `${componentKey}.component`,\n validComponents: readComponentKeys(entry),\n });\n }\n\n return branch.component;\n }\n\n const value = entry[componentKey];\n\n if (!value) {\n throw new InvalidComponentError({\n component: componentKey,\n validComponents: readComponentKeys(entry),\n });\n }\n\n return value;\n}\n\nfunction getComponentFromOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(\n config: ResourceConfigMap<Resources>,\n options: GetComponentOptions<Resources>,\n): JSX.Element {\n const { resource, component: componentKey = 'component' } = options;\n const resourceEntry = config[resource];\n\n if (!resourceEntry) {\n throw new InvalidResourceError({\n resource,\n validResources: collectConfigResources(config as Record<string, unknown>),\n });\n }\n\n let entry = resourceEntry as ResourceConfigEntry;\n\n if ('subResource' in options && options.subResource !== undefined) {\n entry = resolveResourceConfigEntry(entry, options.subResource);\n }\n\n return readResourceConfigComponent(entry, componentKey);\n}\n\nfunction getComponentFromPath<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(config: ResourceConfigMap<Resources>, path: string): JSX.Element {\n let cursor: unknown = config;\n\n for (const segment of path.split('.')) {\n if (!isResourceConfigEntry(cursor)) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n const next: unknown = (cursor as Record<string, unknown>)[segment];\n\n if (next === undefined || next === null) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not configured`,\n });\n }\n\n cursor = next;\n }\n\n if (isResourceConfigEntry(cursor) && 'component' in cursor) {\n return readResourceConfigComponent(cursor, 'component');\n }\n\n return cursor as JSX.Element;\n}\n\nexport function createGetComponent<\n Resources extends ReadonlyArray<ResourceDefinition>,\n>(config: ResourceConfigMap<Resources>): GetComponent<Resources> {\n function getComponent(options: GetComponentOptions<Resources>): JSX.Element;\n function getComponent(path: ResourceConfigPath<Resources>): JSX.Element;\n function getComponent(\n options: GetComponentOptions<Resources> | ResourceConfigPath<Resources>,\n ): JSX.Element {\n if (typeof options === 'string') {\n return getComponentFromPath(config, options);\n }\n\n return getComponentFromOptions(config, options);\n }\n\n const forResource: GetComponentForResource<Resources> = (bound) => {\n return (options) =>\n getComponentFromOptions(config, {\n ...bound,\n ...options,\n } as unknown as GetComponentOptions<Resources>);\n };\n\n return Object.assign(getComponent, { forResource });\n}\n\nfunction createResourceConfigImpl<\n Resources extends ReadonlyArray<ResourceDefinition>,\n const Config extends ResourceConfigMap<Resources>,\n>(\n config: ResourceConfigInput<Resources, Config>,\n): CreatedResourceConfig<Resources, Config> {\n const configRecord = config as Record<string, unknown>;\n\n if (collectConfigResources(configRecord).length === 0) {\n throw new InvalidConfigError({\n reason: 'A resource config must configure at least one resource',\n config,\n });\n }\n\n type Created = CreatedResourceConfig<Resources, Config>;\n\n return {\n config,\n getComponent: createGetComponent(config),\n createGetComponent: ((path: string) =>\n createGetComponentForPath(\n configRecord,\n path,\n )) as unknown as Created['createGetComponent'],\n forPaths: createForPaths(configRecord) as unknown as Created['forPaths'],\n isSubResourceKey: createIsSubResourceKey(\n configRecord,\n ) as unknown as Created['isSubResourceKey'],\n subResources: collectAllSubResourceKeys(\n configRecord,\n ) as unknown as Created['subResources'],\n };\n}\n\nfunction createResourceConfigForResource<\n Resources extends ReadonlyArray<ResourceDefinition>,\n const Resource extends LayoutResourceKey<Resources>,\n const Config extends ResourceConfig<Resources>[Resource],\n>(\n resource: Resource,\n config: Config,\n): ResourceConfigFragment<Resource, Config> {\n return { [resource]: config } as ResourceConfigFragment<Resource, Config>;\n}\n\n/**\n * Creates a validated resource configuration. Use `.forResource()` to build\n * independently declared, type-safe fragments that can be spread into the\n * complete configuration object.\n *\n * @param config - Resource entries to validate and expose through lookup helpers.\n * @returns The original config plus typed component and path lookup helpers.\n *\n * @example\n * ```tsx\n * const users = createResourceConfig.forResource('users', {\n * component: <Users />,\n * });\n *\n * const config = createResourceConfig({ ...users });\n * ```\n */\nexport const createResourceConfig = Object.assign(createResourceConfigImpl, {\n forResource: createResourceConfigForResource,\n});\n"],"mappings":";;;;;;;;;;;;AA4YA,SAAS,sBAAsB,OAA8C;CAC3E,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,2BACP,OACA,aACqB;CACrB,MAAM,MAAM,OAAO,gBAAgB,WAAW,cAAc,YAAY;CACxE,MAAM,OAAO,MAAM;CAEnB,IAAI,CAAC,sBAAsB,IAAI,GAC7B,MAAM,IAAI,wBAAwB;EAChC,aAAa;EACb,mBAAmB,oBAAoB,KAAK;CAC9C,CAAC;CAGH,IAAI,OAAO,gBAAgB,UACzB,OAAO;CAGT,OAAO,2BAA2B,MAAM,YAAY,WAAW;AACjE;AAEA,SAAS,4BACP,OACA,cACa;CACb,IAAI,iBAAiB,SAAS,iBAAiB,UAAU;EACvD,MAAM,SAAS,MAAM;EAErB,IAAI,CAAC,QAAQ,WACX,MAAM,IAAI,sBAAsB;GAC9B,WAAW,GAAG,aAAa;GAC3B,iBAAiB,kBAAkB,KAAK;EAC1C,CAAC;EAGH,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,MAAM;CAEpB,IAAI,CAAC,OACH,MAAM,IAAI,sBAAsB;EAC9B,WAAW;EACX,iBAAiB,kBAAkB,KAAK;CAC1C,CAAC;CAGH,OAAO;AACT;AAEA,SAAS,wBAGP,QACA,SACa;CACb,MAAM,EAAE,UAAU,WAAW,eAAe,gBAAgB;CAC5D,MAAM,gBAAgB,OAAO;CAE7B,IAAI,CAAC,eACH,MAAM,IAAI,qBAAqB;EAC7B;EACA,gBAAgB,uBAAuB,MAAiC;CAC1E,CAAC;CAGH,IAAI,QAAQ;CAEZ,IAAI,iBAAiB,WAAW,QAAQ,gBAAgB,QACtD,QAAQ,2BAA2B,OAAO,QAAQ,WAAW;CAG/D,OAAO,4BAA4B,OAAO,YAAY;AACxD;AAEA,SAAS,qBAEP,QAAsC,MAA2B;CACjE,IAAI,SAAkB;CAEtB,KAAK,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;EACrC,IAAI,CAAC,sBAAsB,MAAM,GAC/B,MAAM,IAAI,iBAAiB;GACzB;GACA,QAAQ,IAAI,QAAQ;EACtB,CAAC;EAGH,MAAM,OAAiB,OAAmC;EAE1D,IAAI,SAAS,UAAa,SAAS,MACjC,MAAM,IAAI,iBAAiB;GACzB;GACA,QAAQ,IAAI,QAAQ;EACtB,CAAC;EAGH,SAAS;CACX;CAEA,IAAI,sBAAsB,MAAM,KAAK,eAAe,QAClD,OAAO,4BAA4B,QAAQ,WAAW;CAGxD,OAAO;AACT;AAEA,SAAgB,mBAEd,QAA+D;CAG/D,SAAS,aACP,SACa;EACb,IAAI,OAAO,YAAY,UACrB,OAAO,qBAAqB,QAAQ,OAAO;EAG7C,OAAO,wBAAwB,QAAQ,OAAO;CAChD;CAEA,MAAM,eAAmD,UAAU;EACjE,QAAQ,YACN,wBAAwB,QAAQ;GAC9B,GAAG;GACH,GAAG;EACL,CAA8C;CAClD;CAEA,OAAO,OAAO,OAAO,cAAc,EAAE,YAAY,CAAC;AACpD;AAEA,SAAS,yBAIP,QAC0C;CAC1C,MAAM,eAAe;CAErB,IAAI,uBAAuB,YAAY,EAAE,WAAW,GAClD,MAAM,IAAI,mBAAmB;EAC3B,QAAQ;EACR;CACF,CAAC;CAKH,OAAO;EACL;EACA,cAAc,mBAAmB,MAAM;EACvC,sBAAsB,SACpB,0BACE,cACA,IACF;EACF,UAAU,eAAe,YAAY;EACrC,kBAAkB,uBAChB,YACF;EACA,cAAc,0BACZ,YACF;CACF;AACF;AAEA,SAAS,gCAKP,UACA,QAC0C;CAC1C,OAAO,GAAG,WAAW,OAAO;AAC9B;;;;;;;;;;;;;;;;;;AAmBA,MAAa,uBAAuB,OAAO,OAAO,0BAA0B,EAC1E,aAAa,gCACf,CAAC"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { AnyRenderStateDefinition, CreateLayoutBuildingBlocks, CreateLayoutBuildingBlocksArguments, CreateLayoutBuildingBlocksResult, CreateRenderState, CreateRenderStateBuilderArguments, CreateRenderStateBuilderResult, DeferredRenderState, DefineLayoutComposables, LayoutComposablesDefinition, LazyRenderState, RenderStateCallback, RenderStateComposablesDefinition, RenderStateContext, RenderStateFragment, RenderStateProps } from "./render-state.cjs";
|
|
1
2
|
import { BaseResourceConfigComponents, ResourceConfig, ResourceConfigComponentKey, ResourceConfigComponents, ResourceConfigInput, ResourceConfigMap, SharedResourceConfigOptions, SubResourceConfig, SubResourceConfigComponentsFor } from "./types.cjs";
|
|
2
3
|
import { ComponentPathVariable, ExtractPathVariables, PATH_VARIABLE_IDENTIFIER, PathHasVariables, PathVariable, ResourcePathVariable, SubResourcePathVariable } from "./paths/variables.cjs";
|
|
3
4
|
import { ConfigResourceKeys, ConfigSubResourceKeys, ParameterizedEntryPath, ParameterizedResourcePath, PathComponentKeys, PathResourceCandidates, PathSubResourceKeys, PathVariables, PathVariablesForResource, ResolveParameterizedPathValue, ResourceSubResourceKeys } from "./paths/types.cjs";
|
|
4
5
|
import { CreateGetComponentForPath, GetComponentForPath, GetComponentForPathApi, PathValueComponentGetter, PathVariableGuards, ResolvedPathValue } from "./for-paths/get-component-for-path.cjs";
|
|
5
6
|
import { ForPaths, ForPathsRenderContext, RenderedPathComponent, RenderedPathComponentHOF, TargetPathBuilder } from "./for-paths/builder.cjs";
|
|
6
7
|
import { IsSubResourceKey, IsSubResourceKeyOptions, IsSubResourceKeyResult } from "./is-sub-resource-key.cjs";
|
|
7
|
-
import { ComponentTypes, CreateResourceConfigFn, CreatedResourceConfig, GetComponent, GetComponentAtBound, GetComponentForResource, GetComponentForResourceOptions, GetComponentOptions, GetComponentOptionsForResource, ResourceConfigPath, ResourceFromGetComponentBound, SubResourceFromGetComponentBound, ValidateForResourceBound } from "./get-component.cjs";
|
|
8
|
+
import { ComponentTypes, CreateResourceConfigFn, CreateResourceConfigForResource, CreatedResourceConfig, GetComponent, GetComponentAtBound, GetComponentForResource, GetComponentForResourceOptions, GetComponentOptions, GetComponentOptionsForResource, ResourceConfigFragment, ResourceConfigPath, ResourceFromGetComponentBound, SubResourceFromGetComponentBound, ValidateForResourceBound } from "./get-component.cjs";
|
|
8
9
|
import { CreateResourceLinkConfig, CreateResourceLinkGroupInput, CreateResourceLinkGroupOptions, CreateResourceLinkOptions, CreateResourceLinksFn, CreateResourceLinksWithGroups, CreateResourceLinksWithGroupsFn, CreateResourceLinksWithGroupsResult, CreateResourceMap, CreateResourceMapFn, CreatedResourceHref, CreatedResourceLink, CreatedResourceLinkBase, CreatedResourceMap, DefineResourceIconFn, DefineResourceIconInputHelpers, DefineResourceIconInputResult, FindResolvedResourceGroupResult, FindResourceGroupResult, InferHashFromResourceLinkHref, ResolveResourceIconInput, ResolvedResourceLinksWithGroups, ResolvedResourceLinksWithGroupsResult, ResourceAnchorLinkFn, ResourceIcon, ResourceIconFn, ResourceLinkHref, ValidateResource } from "./create-resource-links.cjs";
|
|
9
|
-
import { CreateResourceLayoutFn, CreateResourceLayoutMakeComposableOptions, CreateResourceLayoutWithLayoutOptions, DefineResourceLayout, DefineResourceLayoutFn, DefineResourceLayoutForResources, DefineResourceLayoutForResourcesFactory, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayoutWithShell, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutProvider, ResourceLayoutProviderProps, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, defineResourceLayout } from "./define-layout.cjs";
|
|
10
|
+
import { ContextualInlineRenderState, ContextualInlineRenderStates, CreateResourceLayoutFn, CreateResourceLayoutMakeComposableOptions, CreateResourceLayoutWithLayoutOptions, CreateViewMapBuiltInRenderStatesOptions, CreateViewMapFunctionRenderOptions, CreateViewMapInlineRenderStatesOptions, CreateViewMapOptions, CreateViewMapRenderStatesOptions, DefineResourceLayout, DefineResourceLayoutFn, DefineResourceLayoutForResources, DefineResourceLayoutForResourcesFactory, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayout, DefinedResourceLayoutWithShell, LayoutDefinition, LayoutIncludeProps, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutProvider, ResourceLayoutProviderProps, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, TypedBuiltInRenderStates, TypedInlineRenderState, defineResourceLayout } from "./define-layout.cjs";
|
|
11
|
+
import { LazyResourceErrorComponent, LazyResourceLayoutComponent, LazyResourceLayoutOptions, LazyResourceLoadingComponent, LazyResourceStates } from "./lazy-resource-layout.cjs";
|
|
12
|
+
import { BoundDefineResourceLayout, CreateDefinedResourceLayoutOptions, CreateDefinedResourceLayoutResult, DefineLazyDefaults, DefinedResourceLayoutInput, NonEmptyResources, createDefinedResourceLayout } from "./create-defined-resource-layout.cjs";
|
|
10
13
|
import { ResourceLayoutShellPublisher } from "./shell-context.cjs";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { AnyRenderStateDefinition, CreateLayoutBuildingBlocks, CreateLayoutBuildingBlocksArguments, CreateLayoutBuildingBlocksResult, CreateRenderState, CreateRenderStateBuilderArguments, CreateRenderStateBuilderResult, DeferredRenderState, DefineLayoutComposables, LayoutComposablesDefinition, LazyRenderState, RenderStateCallback, RenderStateComposablesDefinition, RenderStateContext, RenderStateFragment, RenderStateProps } from "./render-state.mjs";
|
|
1
2
|
import { BaseResourceConfigComponents, ResourceConfig, ResourceConfigComponentKey, ResourceConfigComponents, ResourceConfigInput, ResourceConfigMap, SharedResourceConfigOptions, SubResourceConfig, SubResourceConfigComponentsFor } from "./types.mjs";
|
|
2
3
|
import { ComponentPathVariable, ExtractPathVariables, PATH_VARIABLE_IDENTIFIER, PathHasVariables, PathVariable, ResourcePathVariable, SubResourcePathVariable } from "./paths/variables.mjs";
|
|
3
4
|
import { ConfigResourceKeys, ConfigSubResourceKeys, ParameterizedEntryPath, ParameterizedResourcePath, PathComponentKeys, PathResourceCandidates, PathSubResourceKeys, PathVariables, PathVariablesForResource, ResolveParameterizedPathValue, ResourceSubResourceKeys } from "./paths/types.mjs";
|
|
4
5
|
import { CreateGetComponentForPath, GetComponentForPath, GetComponentForPathApi, PathValueComponentGetter, PathVariableGuards, ResolvedPathValue } from "./for-paths/get-component-for-path.mjs";
|
|
5
6
|
import { ForPaths, ForPathsRenderContext, RenderedPathComponent, RenderedPathComponentHOF, TargetPathBuilder } from "./for-paths/builder.mjs";
|
|
6
7
|
import { IsSubResourceKey, IsSubResourceKeyOptions, IsSubResourceKeyResult } from "./is-sub-resource-key.mjs";
|
|
7
|
-
import { ComponentTypes, CreateResourceConfigFn, CreatedResourceConfig, GetComponent, GetComponentAtBound, GetComponentForResource, GetComponentForResourceOptions, GetComponentOptions, GetComponentOptionsForResource, ResourceConfigPath, ResourceFromGetComponentBound, SubResourceFromGetComponentBound, ValidateForResourceBound } from "./get-component.mjs";
|
|
8
|
+
import { ComponentTypes, CreateResourceConfigFn, CreateResourceConfigForResource, CreatedResourceConfig, GetComponent, GetComponentAtBound, GetComponentForResource, GetComponentForResourceOptions, GetComponentOptions, GetComponentOptionsForResource, ResourceConfigFragment, ResourceConfigPath, ResourceFromGetComponentBound, SubResourceFromGetComponentBound, ValidateForResourceBound } from "./get-component.mjs";
|
|
8
9
|
import { CreateResourceLinkConfig, CreateResourceLinkGroupInput, CreateResourceLinkGroupOptions, CreateResourceLinkOptions, CreateResourceLinksFn, CreateResourceLinksWithGroups, CreateResourceLinksWithGroupsFn, CreateResourceLinksWithGroupsResult, CreateResourceMap, CreateResourceMapFn, CreatedResourceHref, CreatedResourceLink, CreatedResourceLinkBase, CreatedResourceMap, DefineResourceIconFn, DefineResourceIconInputHelpers, DefineResourceIconInputResult, FindResolvedResourceGroupResult, FindResourceGroupResult, InferHashFromResourceLinkHref, ResolveResourceIconInput, ResolvedResourceLinksWithGroups, ResolvedResourceLinksWithGroupsResult, ResourceAnchorLinkFn, ResourceIcon, ResourceIconFn, ResourceLinkHref, ValidateResource } from "./create-resource-links.mjs";
|
|
9
|
-
import { CreateResourceLayoutFn, CreateResourceLayoutMakeComposableOptions, CreateResourceLayoutWithLayoutOptions, DefineResourceLayout, DefineResourceLayoutFn, DefineResourceLayoutForResources, DefineResourceLayoutForResourcesFactory, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayoutWithShell, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutProvider, ResourceLayoutProviderProps, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, defineResourceLayout } from "./define-layout.mjs";
|
|
10
|
+
import { ContextualInlineRenderState, ContextualInlineRenderStates, CreateResourceLayoutFn, CreateResourceLayoutMakeComposableOptions, CreateResourceLayoutWithLayoutOptions, CreateViewMapBuiltInRenderStatesOptions, CreateViewMapFunctionRenderOptions, CreateViewMapInlineRenderStatesOptions, CreateViewMapOptions, CreateViewMapRenderStatesOptions, DefineResourceLayout, DefineResourceLayoutFn, DefineResourceLayoutForResources, DefineResourceLayoutForResourcesFactory, DefineResourceLayoutWithLayoutFn, DefineResourcesFn, DefinedResourceLayout, DefinedResourceLayoutWithShell, LayoutDefinition, LayoutIncludeProps, LayoutRenderResourcesAccessor, LayoutResourcesAccessor, ResourceLayoutProvider, ResourceLayoutProviderProps, ResourceLayoutShellComponent, ResourceLayoutShellProps, ResourceSelection, ShellLayoutDefinition, ShellRenderContext, TypedBuiltInRenderStates, TypedInlineRenderState, defineResourceLayout } from "./define-layout.mjs";
|
|
11
|
+
import { LazyResourceErrorComponent, LazyResourceLayoutComponent, LazyResourceLayoutOptions, LazyResourceLoadingComponent, LazyResourceStates } from "./lazy-resource-layout.mjs";
|
|
12
|
+
import { BoundDefineResourceLayout, CreateDefinedResourceLayoutOptions, CreateDefinedResourceLayoutResult, DefineLazyDefaults, DefinedResourceLayoutInput, NonEmptyResources, createDefinedResourceLayout } from "./create-defined-resource-layout.mjs";
|
|
10
13
|
import { ResourceLayoutShellPublisher } from "./shell-context.mjs";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const require_layout_configuration_error = require('../errors/layout-configuration-error.cjs');
|
|
2
|
+
const require_resource_state_error = require('../errors/resource-state-error.cjs');
|
|
3
|
+
const require_render_state = require('./render-state.cjs');
|
|
4
|
+
let react = require("react");
|
|
5
|
+
let _jfdevelops_react_layout_validator = require("@jfdevelops/react-layout-validator");
|
|
6
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
+
|
|
8
|
+
//#region src/create-config/lazy-resource-layout.tsx
|
|
9
|
+
var LazyResourceErrorBoundary = class extends react.Component {
|
|
10
|
+
state = {};
|
|
11
|
+
static getDerivedStateFromError(error) {
|
|
12
|
+
return { error };
|
|
13
|
+
}
|
|
14
|
+
retry = () => {
|
|
15
|
+
this.props.onRetry();
|
|
16
|
+
this.setState({ error: void 0 });
|
|
17
|
+
};
|
|
18
|
+
render() {
|
|
19
|
+
if (this.state.error !== void 0) {
|
|
20
|
+
const { ErrorComponent } = this.props;
|
|
21
|
+
if (!ErrorComponent) throw this.state.error;
|
|
22
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorComponent, {
|
|
23
|
+
error: this.state.error,
|
|
24
|
+
retry: this.retry
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return this.props.children;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function findDefinedLayoutComponent(module) {
|
|
31
|
+
const components = Object.values(module).filter((value) => typeof value === "function" && require_render_state.definedResourceLayoutComponentSymbol in value);
|
|
32
|
+
if (components.length !== 1) throw new require_layout_configuration_error.LayoutConfigurationError(`A lazy resource module must export exactly one created resource layout component; received ${components.length}`);
|
|
33
|
+
return components[0];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a cached lazy proxy for a resource component and all of its compound
|
|
37
|
+
* state components. This is normally called through
|
|
38
|
+
* `defineResourceLayout.lazy(...)`, which supplies shared defaults.
|
|
39
|
+
*/
|
|
40
|
+
function createLazyResourceLayout(options, defaults) {
|
|
41
|
+
const runtimeOptions = options;
|
|
42
|
+
let loadedComponentPromise;
|
|
43
|
+
const stateComponents = /* @__PURE__ */ new Map();
|
|
44
|
+
const resolvedStates = {
|
|
45
|
+
...defaults,
|
|
46
|
+
...runtimeOptions.states
|
|
47
|
+
};
|
|
48
|
+
function loadComponent() {
|
|
49
|
+
loadedComponentPromise ??= runtimeOptions.load().then(findDefinedLayoutComponent).catch((error) => {
|
|
50
|
+
throw new require_resource_state_error.ResourceModuleLoadError(runtimeOptions.resource, error);
|
|
51
|
+
});
|
|
52
|
+
return loadedComponentPromise;
|
|
53
|
+
}
|
|
54
|
+
function resetLoad() {
|
|
55
|
+
loadedComponentPromise = void 0;
|
|
56
|
+
stateComponents.clear();
|
|
57
|
+
}
|
|
58
|
+
function createMemberComponent(member) {
|
|
59
|
+
function createLazyComponent() {
|
|
60
|
+
return (0, react.lazy)(async () => {
|
|
61
|
+
const component = await loadComponent();
|
|
62
|
+
const selected = member === void 0 ? component : component[member];
|
|
63
|
+
if (typeof selected !== "function") throw new require_layout_configuration_error.LayoutConfigurationError(`Lazy resource "${runtimeOptions.resource}" does not define state "${String(member)}"`);
|
|
64
|
+
return { default: selected };
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function LazyMember(props) {
|
|
68
|
+
const [, rerender] = (0, react.useReducer)((version) => version + 1, 0);
|
|
69
|
+
const LazyComponent = (0, react.useRef)(createLazyComponent());
|
|
70
|
+
const LoadingComponent = resolvedStates.Loading;
|
|
71
|
+
const CurrentLazyComponent = LazyComponent.current;
|
|
72
|
+
function retry() {
|
|
73
|
+
resetLoad();
|
|
74
|
+
LazyComponent.current = createLazyComponent();
|
|
75
|
+
rerender();
|
|
76
|
+
}
|
|
77
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LazyResourceErrorBoundary, {
|
|
78
|
+
ErrorComponent: resolvedStates.Error,
|
|
79
|
+
onRetry: retry,
|
|
80
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
81
|
+
fallback: LoadingComponent ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoadingComponent, {}) : null,
|
|
82
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CurrentLazyComponent, { ...props })
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const resourceName = runtimeOptions.resource;
|
|
87
|
+
LazyMember.displayName = member === void 0 ? `LazyResource(${resourceName})` : `LazyResource(${resourceName}).${String(member)}`;
|
|
88
|
+
return LazyMember;
|
|
89
|
+
}
|
|
90
|
+
const Component = createMemberComponent();
|
|
91
|
+
const unsupportedStatics = new Set([
|
|
92
|
+
"makeComposable",
|
|
93
|
+
"props",
|
|
94
|
+
"resource",
|
|
95
|
+
_jfdevelops_react_layout_validator.componentPropDefinitionsSymbol,
|
|
96
|
+
require_render_state.definedResourceLayoutComponentSymbol
|
|
97
|
+
]);
|
|
98
|
+
return new Proxy(Component, { get(target, property, receiver) {
|
|
99
|
+
if (Reflect.has(target, property)) return Reflect.get(target, property, receiver);
|
|
100
|
+
if (typeof property !== "string" || unsupportedStatics.has(property)) return;
|
|
101
|
+
const existing = stateComponents.get(property);
|
|
102
|
+
if (existing) return existing;
|
|
103
|
+
const stateComponent = createMemberComponent(property);
|
|
104
|
+
stateComponents.set(property, stateComponent);
|
|
105
|
+
return stateComponent;
|
|
106
|
+
} });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
exports.createLazyResourceLayout = createLazyResourceLayout;
|
|
111
|
+
//# sourceMappingURL=lazy-resource-layout.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-resource-layout.cjs","names":["Component","definedResourceLayoutComponentSymbol","LayoutConfigurationError","ResourceModuleLoadError","Suspense","componentPropDefinitionsSymbol"],"sources":["../../src/create-config/lazy-resource-layout.tsx"],"sourcesContent":["import {\n Component,\n type ComponentProps,\n type ComponentType,\n type JSX,\n lazy,\n Suspense,\n useReducer,\n useRef,\n} from 'react';\nimport {\n LayoutConfigurationError,\n ResourceModuleLoadError,\n} from '../errors';\nimport { componentPropDefinitionsSymbol } from '@jfdevelops/react-layout-validator';\nimport type { ResourcePath, ResourceDefinition } from '../resource';\nimport { definedResourceLayoutComponentSymbol } from './render-state';\n\ntype DefinedLayoutComponent = ComponentType<any> & {\n readonly [definedResourceLayoutComponentSymbol]: true;\n};\n\ntype DefinedLayoutFromModule<Module> = Extract<\n Module[keyof Module],\n DefinedLayoutComponent\n>;\n\ntype LazyUnsupportedStatic =\n | 'makeComposable'\n | 'props'\n | 'resource'\n | typeof componentPropDefinitionsSymbol\n | typeof definedResourceLayoutComponentSymbol;\n\n/**\n * Public surface available before a lazy module resolves. Component metadata\n * and composition factories are intentionally omitted because they cannot be\n * read synchronously without loading the module.\n */\nexport type LazyResourceLayoutComponent<Module> = ComponentType<\n ComponentProps<DefinedLayoutFromModule<Module>>\n> &\n Pick<\n DefinedLayoutFromModule<Module>,\n Exclude<keyof DefinedLayoutFromModule<Module>, LazyUnsupportedStatic>\n >;\n\n/** Component rendered while a resource module or one of its states is loading. */\nexport type LazyResourceLoadingComponent = ComponentType;\n/**\n * Component rendered when a resource module cannot be loaded. Calling\n * `retry` discards React's rejected lazy cache entry and imports it again.\n */\nexport type LazyResourceErrorComponent = ComponentType<{\n error: unknown;\n retry: () => void;\n}>;\n\n/** Loading and module-error UI used around a lazy resource component. */\nexport type LazyResourceStates = {\n Error?: LazyResourceErrorComponent;\n Loading?: LazyResourceLoadingComponent;\n};\n\n/**\n * Configuration for a lazy resource component. `resource` is checked against\n * the bound resource paths, including dot-delimited subresources, and `load`\n * must resolve to a module with exactly one created resource component.\n */\nexport type LazyResourceLayoutOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Module extends Record<string, unknown>,\n> = {\n load: () => Promise<Module>;\n resource: ResourcePath<Resources>;\n states?: LazyResourceStates;\n};\n\ntype BoundaryProps = {\n children: JSX.Element;\n ErrorComponent?: LazyResourceErrorComponent;\n onRetry: () => void;\n};\n\ntype BoundaryState = {\n error?: unknown;\n};\n\nclass LazyResourceErrorBoundary extends Component<\n BoundaryProps,\n BoundaryState\n> {\n state: BoundaryState = {};\n\n static getDerivedStateFromError(error: unknown): BoundaryState {\n return { error };\n }\n\n retry = () => {\n this.props.onRetry();\n this.setState({ error: undefined });\n };\n\n render() {\n if (this.state.error !== undefined) {\n const { ErrorComponent } = this.props;\n\n if (!ErrorComponent) {\n throw this.state.error;\n }\n\n return <ErrorComponent error={this.state.error} retry={this.retry} />;\n }\n\n return this.props.children;\n }\n}\n\nfunction findDefinedLayoutComponent(module: Record<string, unknown>) {\n const components = Object.values(module).filter(\n (value): value is DefinedLayoutComponent =>\n typeof value === 'function' &&\n definedResourceLayoutComponentSymbol in value,\n );\n\n if (components.length !== 1) {\n throw new LayoutConfigurationError(\n `A lazy resource module must export exactly one created resource layout component; received ${components.length}`,\n );\n }\n\n return components[0];\n}\n\n/**\n * Creates a cached lazy proxy for a resource component and all of its compound\n * state components. This is normally called through\n * `defineResourceLayout.lazy(...)`, which supplies shared defaults.\n */\nexport function createLazyResourceLayout<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Module extends Record<string, unknown>,\n>(\n options: LazyResourceLayoutOptions<Resources, Module>,\n defaults: LazyResourceStates,\n): LazyResourceLayoutComponent<Module> {\n const runtimeOptions = options as {\n load: () => Promise<Record<string, unknown>>;\n resource: string;\n states?: LazyResourceStates;\n };\n let loadedComponentPromise: Promise<DefinedLayoutComponent> | undefined;\n const stateComponents = new Map<PropertyKey, ComponentType<any>>();\n const resolvedStates = { ...defaults, ...runtimeOptions.states };\n\n function loadComponent() {\n loadedComponentPromise ??= runtimeOptions\n .load()\n .then(findDefinedLayoutComponent)\n .catch((error: unknown) => {\n throw new ResourceModuleLoadError(runtimeOptions.resource, error);\n });\n\n return loadedComponentPromise;\n }\n\n function resetLoad() {\n loadedComponentPromise = undefined;\n stateComponents.clear();\n }\n\n function createMemberComponent(member?: PropertyKey) {\n function createLazyComponent() {\n return lazy(async () => {\n const component = await loadComponent();\n const selected =\n member === undefined\n ? component\n : (component as unknown as Record<PropertyKey, unknown>)[member];\n\n if (typeof selected !== 'function') {\n throw new LayoutConfigurationError(\n `Lazy resource \"${runtimeOptions.resource}\" does not define state \"${String(member)}\"`,\n );\n }\n\n return { default: selected as ComponentType<any> };\n });\n }\n\n function LazyMember(props: Record<string, unknown>) {\n const [, rerender] = useReducer((version) => version + 1, 0);\n const LazyComponent = useRef(createLazyComponent());\n const LoadingComponent = resolvedStates.Loading;\n const CurrentLazyComponent = LazyComponent.current;\n\n function retry() {\n resetLoad();\n LazyComponent.current = createLazyComponent();\n rerender();\n }\n\n return (\n <LazyResourceErrorBoundary\n ErrorComponent={resolvedStates.Error}\n onRetry={retry}\n >\n <Suspense\n fallback={LoadingComponent ? <LoadingComponent /> : null}\n >\n <CurrentLazyComponent {...props} />\n </Suspense>\n </LazyResourceErrorBoundary>\n );\n }\n\n const resourceName = runtimeOptions.resource;\n LazyMember.displayName =\n member === undefined\n ? `LazyResource(${resourceName})`\n : `LazyResource(${resourceName}).${String(member)}`;\n\n return LazyMember;\n }\n\n const Component = createMemberComponent();\n const unsupportedStatics = new Set<PropertyKey>([\n 'makeComposable',\n 'props',\n 'resource',\n componentPropDefinitionsSymbol,\n definedResourceLayoutComponentSymbol,\n ]);\n\n return new Proxy(Component, {\n get(target, property, receiver) {\n if (Reflect.has(target, property)) {\n return Reflect.get(target, property, receiver);\n }\n\n if (typeof property !== 'string' || unsupportedStatics.has(property)) {\n return undefined;\n }\n\n const existing = stateComponents.get(property);\n\n if (existing) {\n return existing;\n }\n\n const stateComponent = createMemberComponent(property);\n stateComponents.set(property, stateComponent);\n\n return stateComponent;\n },\n }) as unknown as LazyResourceLayoutComponent<Module>;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ResourceDefinition, ResourcePath } from "../resource.cjs";
|
|
2
|
+
import { definedResourceLayoutComponentSymbol } from "./render-state.cjs";
|
|
3
|
+
import { ComponentProps, ComponentType } from "react";
|
|
4
|
+
import { componentPropDefinitionsSymbol } from "@jfdevelops/react-layout-validator";
|
|
5
|
+
|
|
6
|
+
//#region src/create-config/lazy-resource-layout.d.ts
|
|
7
|
+
type DefinedLayoutComponent = ComponentType<any> & {
|
|
8
|
+
readonly [definedResourceLayoutComponentSymbol]: true;
|
|
9
|
+
};
|
|
10
|
+
type DefinedLayoutFromModule<Module> = Extract<Module[keyof Module], DefinedLayoutComponent>;
|
|
11
|
+
type LazyUnsupportedStatic = 'makeComposable' | 'props' | 'resource' | typeof componentPropDefinitionsSymbol | typeof definedResourceLayoutComponentSymbol;
|
|
12
|
+
/**
|
|
13
|
+
* Public surface available before a lazy module resolves. Component metadata
|
|
14
|
+
* and composition factories are intentionally omitted because they cannot be
|
|
15
|
+
* read synchronously without loading the module.
|
|
16
|
+
*/
|
|
17
|
+
type LazyResourceLayoutComponent<Module> = ComponentType<ComponentProps<DefinedLayoutFromModule<Module>>> & Pick<DefinedLayoutFromModule<Module>, Exclude<keyof DefinedLayoutFromModule<Module>, LazyUnsupportedStatic>>;
|
|
18
|
+
/** Component rendered while a resource module or one of its states is loading. */
|
|
19
|
+
type LazyResourceLoadingComponent = ComponentType;
|
|
20
|
+
/**
|
|
21
|
+
* Component rendered when a resource module cannot be loaded. Calling
|
|
22
|
+
* `retry` discards React's rejected lazy cache entry and imports it again.
|
|
23
|
+
*/
|
|
24
|
+
type LazyResourceErrorComponent = ComponentType<{
|
|
25
|
+
error: unknown;
|
|
26
|
+
retry: () => void;
|
|
27
|
+
}>;
|
|
28
|
+
/** Loading and module-error UI used around a lazy resource component. */
|
|
29
|
+
type LazyResourceStates = {
|
|
30
|
+
Error?: LazyResourceErrorComponent;
|
|
31
|
+
Loading?: LazyResourceLoadingComponent;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for a lazy resource component. `resource` is checked against
|
|
35
|
+
* the bound resource paths, including dot-delimited subresources, and `load`
|
|
36
|
+
* must resolve to a module with exactly one created resource component.
|
|
37
|
+
*/
|
|
38
|
+
type LazyResourceLayoutOptions<Resources extends ReadonlyArray<ResourceDefinition>, Module extends Record<string, unknown>> = {
|
|
39
|
+
load: () => Promise<Module>;
|
|
40
|
+
resource: ResourcePath<Resources>;
|
|
41
|
+
states?: LazyResourceStates;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Creates a cached lazy proxy for a resource component and all of its compound
|
|
45
|
+
* state components. This is normally called through
|
|
46
|
+
* `defineResourceLayout.lazy(...)`, which supplies shared defaults.
|
|
47
|
+
*/
|
|
48
|
+
declare function createLazyResourceLayout<Resources extends ReadonlyArray<ResourceDefinition>, Module extends Record<string, unknown>>(options: LazyResourceLayoutOptions<Resources, Module>, defaults: LazyResourceStates): LazyResourceLayoutComponent<Module>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { LazyResourceErrorComponent, LazyResourceLayoutComponent, LazyResourceLayoutOptions, LazyResourceLoadingComponent, LazyResourceStates, createLazyResourceLayout };
|
|
51
|
+
//# sourceMappingURL=lazy-resource-layout.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-resource-layout.d.cts","names":[],"sources":["../../src/create-config/lazy-resource-layout.tsx"],"mappings":";;;;;;KAkBK,sBAAA,GAAyB,aAAA;EAAA,UAClB,oCAAoC;AAAA;AAAA,KAG3C,uBAAA,WAAkC,OAAA,CACrC,MAAA,OAAa,MAAA,GACb,sBAAA;AAAA,KAGG,qBAAA,oDAIM,8BAAA,UACA,oCAAoC;;AAbC;AAAA;;;KAoBpC,2BAAA,WAAsC,aAAA,CAChD,cAAA,CAAe,uBAAA,CAAwB,MAAA,MAEvC,IAAA,CACE,uBAAA,CAAwB,MAAA,GACxB,OAAA,OAAc,uBAAA,CAAwB,MAAA,GAAS,qBAAA;;KAIvC,4BAAA,GAA+B,aAAa;;;;;KAK5C,0BAAA,GAA6B,aAAa;EACpD,KAAA;EACA,KAAA;AAAA;;KAIU,kBAAA;EACV,KAAA,GAAQ,0BAAA;EACR,OAAA,GAAU,4BAA4B;AAAA;;AA7BO;AAO/C;;;KA8BY,yBAAA,mBACQ,aAAA,CAAc,kBAAA,kBACjB,MAAA;EAEf,IAAA,QAAY,OAAA,CAAQ,MAAA;EACpB,QAAA,EAAU,YAAA,CAAa,SAAA;EACvB,MAAA,GAAS,kBAAA;AAAA;;;;;;iBAgEK,wBAAA,mBACI,aAAA,CAAc,kBAAA,kBACjB,MAAA,mBAEf,OAAA,EAAS,yBAAA,CAA0B,SAAA,EAAW,MAAA,GAC9C,QAAA,EAAU,kBAAA,GACT,2BAAA,CAA4B,MAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ResourceDefinition, ResourcePath } from "../resource.mjs";
|
|
2
|
+
import { definedResourceLayoutComponentSymbol } from "./render-state.mjs";
|
|
3
|
+
import { ComponentProps, ComponentType } from "react";
|
|
4
|
+
import { componentPropDefinitionsSymbol } from "@jfdevelops/react-layout-validator";
|
|
5
|
+
|
|
6
|
+
//#region src/create-config/lazy-resource-layout.d.ts
|
|
7
|
+
type DefinedLayoutComponent = ComponentType<any> & {
|
|
8
|
+
readonly [definedResourceLayoutComponentSymbol]: true;
|
|
9
|
+
};
|
|
10
|
+
type DefinedLayoutFromModule<Module> = Extract<Module[keyof Module], DefinedLayoutComponent>;
|
|
11
|
+
type LazyUnsupportedStatic = 'makeComposable' | 'props' | 'resource' | typeof componentPropDefinitionsSymbol | typeof definedResourceLayoutComponentSymbol;
|
|
12
|
+
/**
|
|
13
|
+
* Public surface available before a lazy module resolves. Component metadata
|
|
14
|
+
* and composition factories are intentionally omitted because they cannot be
|
|
15
|
+
* read synchronously without loading the module.
|
|
16
|
+
*/
|
|
17
|
+
type LazyResourceLayoutComponent<Module> = ComponentType<ComponentProps<DefinedLayoutFromModule<Module>>> & Pick<DefinedLayoutFromModule<Module>, Exclude<keyof DefinedLayoutFromModule<Module>, LazyUnsupportedStatic>>;
|
|
18
|
+
/** Component rendered while a resource module or one of its states is loading. */
|
|
19
|
+
type LazyResourceLoadingComponent = ComponentType;
|
|
20
|
+
/**
|
|
21
|
+
* Component rendered when a resource module cannot be loaded. Calling
|
|
22
|
+
* `retry` discards React's rejected lazy cache entry and imports it again.
|
|
23
|
+
*/
|
|
24
|
+
type LazyResourceErrorComponent = ComponentType<{
|
|
25
|
+
error: unknown;
|
|
26
|
+
retry: () => void;
|
|
27
|
+
}>;
|
|
28
|
+
/** Loading and module-error UI used around a lazy resource component. */
|
|
29
|
+
type LazyResourceStates = {
|
|
30
|
+
Error?: LazyResourceErrorComponent;
|
|
31
|
+
Loading?: LazyResourceLoadingComponent;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for a lazy resource component. `resource` is checked against
|
|
35
|
+
* the bound resource paths, including dot-delimited subresources, and `load`
|
|
36
|
+
* must resolve to a module with exactly one created resource component.
|
|
37
|
+
*/
|
|
38
|
+
type LazyResourceLayoutOptions<Resources extends ReadonlyArray<ResourceDefinition>, Module extends Record<string, unknown>> = {
|
|
39
|
+
load: () => Promise<Module>;
|
|
40
|
+
resource: ResourcePath<Resources>;
|
|
41
|
+
states?: LazyResourceStates;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Creates a cached lazy proxy for a resource component and all of its compound
|
|
45
|
+
* state components. This is normally called through
|
|
46
|
+
* `defineResourceLayout.lazy(...)`, which supplies shared defaults.
|
|
47
|
+
*/
|
|
48
|
+
declare function createLazyResourceLayout<Resources extends ReadonlyArray<ResourceDefinition>, Module extends Record<string, unknown>>(options: LazyResourceLayoutOptions<Resources, Module>, defaults: LazyResourceStates): LazyResourceLayoutComponent<Module>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { LazyResourceErrorComponent, LazyResourceLayoutComponent, LazyResourceLayoutOptions, LazyResourceLoadingComponent, LazyResourceStates, createLazyResourceLayout };
|
|
51
|
+
//# sourceMappingURL=lazy-resource-layout.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-resource-layout.d.mts","names":[],"sources":["../../src/create-config/lazy-resource-layout.tsx"],"mappings":";;;;;;KAkBK,sBAAA,GAAyB,aAAA;EAAA,UAClB,oCAAoC;AAAA;AAAA,KAG3C,uBAAA,WAAkC,OAAA,CACrC,MAAA,OAAa,MAAA,GACb,sBAAA;AAAA,KAGG,qBAAA,oDAIM,8BAAA,UACA,oCAAoC;;AAbC;AAAA;;;KAoBpC,2BAAA,WAAsC,aAAA,CAChD,cAAA,CAAe,uBAAA,CAAwB,MAAA,MAEvC,IAAA,CACE,uBAAA,CAAwB,MAAA,GACxB,OAAA,OAAc,uBAAA,CAAwB,MAAA,GAAS,qBAAA;;KAIvC,4BAAA,GAA+B,aAAa;;;;;KAK5C,0BAAA,GAA6B,aAAa;EACpD,KAAA;EACA,KAAA;AAAA;;KAIU,kBAAA;EACV,KAAA,GAAQ,0BAAA;EACR,OAAA,GAAU,4BAA4B;AAAA;;AA7BO;AAO/C;;;KA8BY,yBAAA,mBACQ,aAAA,CAAc,kBAAA,kBACjB,MAAA;EAEf,IAAA,QAAY,OAAA,CAAQ,MAAA;EACpB,QAAA,EAAU,YAAA,CAAa,SAAA;EACvB,MAAA,GAAS,kBAAA;AAAA;;;;;;iBAgEK,wBAAA,mBACI,aAAA,CAAc,kBAAA,kBACjB,MAAA,mBAEf,OAAA,EAAS,yBAAA,CAA0B,SAAA,EAAW,MAAA,GAC9C,QAAA,EAAU,kBAAA,GACT,2BAAA,CAA4B,MAAA"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { LayoutConfigurationError } from "../errors/layout-configuration-error.mjs";
|
|
2
|
+
import { ResourceModuleLoadError } from "../errors/resource-state-error.mjs";
|
|
3
|
+
import { definedResourceLayoutComponentSymbol } from "./render-state.mjs";
|
|
4
|
+
import { Component, Suspense, lazy, useReducer, useRef } from "react";
|
|
5
|
+
import { componentPropDefinitionsSymbol } from "@jfdevelops/react-layout-validator";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/create-config/lazy-resource-layout.tsx
|
|
9
|
+
var LazyResourceErrorBoundary = class extends Component {
|
|
10
|
+
state = {};
|
|
11
|
+
static getDerivedStateFromError(error) {
|
|
12
|
+
return { error };
|
|
13
|
+
}
|
|
14
|
+
retry = () => {
|
|
15
|
+
this.props.onRetry();
|
|
16
|
+
this.setState({ error: void 0 });
|
|
17
|
+
};
|
|
18
|
+
render() {
|
|
19
|
+
if (this.state.error !== void 0) {
|
|
20
|
+
const { ErrorComponent } = this.props;
|
|
21
|
+
if (!ErrorComponent) throw this.state.error;
|
|
22
|
+
return /* @__PURE__ */ jsx(ErrorComponent, {
|
|
23
|
+
error: this.state.error,
|
|
24
|
+
retry: this.retry
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return this.props.children;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function findDefinedLayoutComponent(module) {
|
|
31
|
+
const components = Object.values(module).filter((value) => typeof value === "function" && definedResourceLayoutComponentSymbol in value);
|
|
32
|
+
if (components.length !== 1) throw new LayoutConfigurationError(`A lazy resource module must export exactly one created resource layout component; received ${components.length}`);
|
|
33
|
+
return components[0];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a cached lazy proxy for a resource component and all of its compound
|
|
37
|
+
* state components. This is normally called through
|
|
38
|
+
* `defineResourceLayout.lazy(...)`, which supplies shared defaults.
|
|
39
|
+
*/
|
|
40
|
+
function createLazyResourceLayout(options, defaults) {
|
|
41
|
+
const runtimeOptions = options;
|
|
42
|
+
let loadedComponentPromise;
|
|
43
|
+
const stateComponents = /* @__PURE__ */ new Map();
|
|
44
|
+
const resolvedStates = {
|
|
45
|
+
...defaults,
|
|
46
|
+
...runtimeOptions.states
|
|
47
|
+
};
|
|
48
|
+
function loadComponent() {
|
|
49
|
+
loadedComponentPromise ??= runtimeOptions.load().then(findDefinedLayoutComponent).catch((error) => {
|
|
50
|
+
throw new ResourceModuleLoadError(runtimeOptions.resource, error);
|
|
51
|
+
});
|
|
52
|
+
return loadedComponentPromise;
|
|
53
|
+
}
|
|
54
|
+
function resetLoad() {
|
|
55
|
+
loadedComponentPromise = void 0;
|
|
56
|
+
stateComponents.clear();
|
|
57
|
+
}
|
|
58
|
+
function createMemberComponent(member) {
|
|
59
|
+
function createLazyComponent() {
|
|
60
|
+
return lazy(async () => {
|
|
61
|
+
const component = await loadComponent();
|
|
62
|
+
const selected = member === void 0 ? component : component[member];
|
|
63
|
+
if (typeof selected !== "function") throw new LayoutConfigurationError(`Lazy resource "${runtimeOptions.resource}" does not define state "${String(member)}"`);
|
|
64
|
+
return { default: selected };
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function LazyMember(props) {
|
|
68
|
+
const [, rerender] = useReducer((version) => version + 1, 0);
|
|
69
|
+
const LazyComponent = useRef(createLazyComponent());
|
|
70
|
+
const LoadingComponent = resolvedStates.Loading;
|
|
71
|
+
const CurrentLazyComponent = LazyComponent.current;
|
|
72
|
+
function retry() {
|
|
73
|
+
resetLoad();
|
|
74
|
+
LazyComponent.current = createLazyComponent();
|
|
75
|
+
rerender();
|
|
76
|
+
}
|
|
77
|
+
return /* @__PURE__ */ jsx(LazyResourceErrorBoundary, {
|
|
78
|
+
ErrorComponent: resolvedStates.Error,
|
|
79
|
+
onRetry: retry,
|
|
80
|
+
children: /* @__PURE__ */ jsx(Suspense, {
|
|
81
|
+
fallback: LoadingComponent ? /* @__PURE__ */ jsx(LoadingComponent, {}) : null,
|
|
82
|
+
children: /* @__PURE__ */ jsx(CurrentLazyComponent, { ...props })
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const resourceName = runtimeOptions.resource;
|
|
87
|
+
LazyMember.displayName = member === void 0 ? `LazyResource(${resourceName})` : `LazyResource(${resourceName}).${String(member)}`;
|
|
88
|
+
return LazyMember;
|
|
89
|
+
}
|
|
90
|
+
const Component = createMemberComponent();
|
|
91
|
+
const unsupportedStatics = new Set([
|
|
92
|
+
"makeComposable",
|
|
93
|
+
"props",
|
|
94
|
+
"resource",
|
|
95
|
+
componentPropDefinitionsSymbol,
|
|
96
|
+
definedResourceLayoutComponentSymbol
|
|
97
|
+
]);
|
|
98
|
+
return new Proxy(Component, { get(target, property, receiver) {
|
|
99
|
+
if (Reflect.has(target, property)) return Reflect.get(target, property, receiver);
|
|
100
|
+
if (typeof property !== "string" || unsupportedStatics.has(property)) return;
|
|
101
|
+
const existing = stateComponents.get(property);
|
|
102
|
+
if (existing) return existing;
|
|
103
|
+
const stateComponent = createMemberComponent(property);
|
|
104
|
+
stateComponents.set(property, stateComponent);
|
|
105
|
+
return stateComponent;
|
|
106
|
+
} });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
export { createLazyResourceLayout };
|
|
111
|
+
//# sourceMappingURL=lazy-resource-layout.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-resource-layout.mjs","names":[],"sources":["../../src/create-config/lazy-resource-layout.tsx"],"sourcesContent":["import {\n Component,\n type ComponentProps,\n type ComponentType,\n type JSX,\n lazy,\n Suspense,\n useReducer,\n useRef,\n} from 'react';\nimport {\n LayoutConfigurationError,\n ResourceModuleLoadError,\n} from '../errors';\nimport { componentPropDefinitionsSymbol } from '@jfdevelops/react-layout-validator';\nimport type { ResourcePath, ResourceDefinition } from '../resource';\nimport { definedResourceLayoutComponentSymbol } from './render-state';\n\ntype DefinedLayoutComponent = ComponentType<any> & {\n readonly [definedResourceLayoutComponentSymbol]: true;\n};\n\ntype DefinedLayoutFromModule<Module> = Extract<\n Module[keyof Module],\n DefinedLayoutComponent\n>;\n\ntype LazyUnsupportedStatic =\n | 'makeComposable'\n | 'props'\n | 'resource'\n | typeof componentPropDefinitionsSymbol\n | typeof definedResourceLayoutComponentSymbol;\n\n/**\n * Public surface available before a lazy module resolves. Component metadata\n * and composition factories are intentionally omitted because they cannot be\n * read synchronously without loading the module.\n */\nexport type LazyResourceLayoutComponent<Module> = ComponentType<\n ComponentProps<DefinedLayoutFromModule<Module>>\n> &\n Pick<\n DefinedLayoutFromModule<Module>,\n Exclude<keyof DefinedLayoutFromModule<Module>, LazyUnsupportedStatic>\n >;\n\n/** Component rendered while a resource module or one of its states is loading. */\nexport type LazyResourceLoadingComponent = ComponentType;\n/**\n * Component rendered when a resource module cannot be loaded. Calling\n * `retry` discards React's rejected lazy cache entry and imports it again.\n */\nexport type LazyResourceErrorComponent = ComponentType<{\n error: unknown;\n retry: () => void;\n}>;\n\n/** Loading and module-error UI used around a lazy resource component. */\nexport type LazyResourceStates = {\n Error?: LazyResourceErrorComponent;\n Loading?: LazyResourceLoadingComponent;\n};\n\n/**\n * Configuration for a lazy resource component. `resource` is checked against\n * the bound resource paths, including dot-delimited subresources, and `load`\n * must resolve to a module with exactly one created resource component.\n */\nexport type LazyResourceLayoutOptions<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Module extends Record<string, unknown>,\n> = {\n load: () => Promise<Module>;\n resource: ResourcePath<Resources>;\n states?: LazyResourceStates;\n};\n\ntype BoundaryProps = {\n children: JSX.Element;\n ErrorComponent?: LazyResourceErrorComponent;\n onRetry: () => void;\n};\n\ntype BoundaryState = {\n error?: unknown;\n};\n\nclass LazyResourceErrorBoundary extends Component<\n BoundaryProps,\n BoundaryState\n> {\n state: BoundaryState = {};\n\n static getDerivedStateFromError(error: unknown): BoundaryState {\n return { error };\n }\n\n retry = () => {\n this.props.onRetry();\n this.setState({ error: undefined });\n };\n\n render() {\n if (this.state.error !== undefined) {\n const { ErrorComponent } = this.props;\n\n if (!ErrorComponent) {\n throw this.state.error;\n }\n\n return <ErrorComponent error={this.state.error} retry={this.retry} />;\n }\n\n return this.props.children;\n }\n}\n\nfunction findDefinedLayoutComponent(module: Record<string, unknown>) {\n const components = Object.values(module).filter(\n (value): value is DefinedLayoutComponent =>\n typeof value === 'function' &&\n definedResourceLayoutComponentSymbol in value,\n );\n\n if (components.length !== 1) {\n throw new LayoutConfigurationError(\n `A lazy resource module must export exactly one created resource layout component; received ${components.length}`,\n );\n }\n\n return components[0];\n}\n\n/**\n * Creates a cached lazy proxy for a resource component and all of its compound\n * state components. This is normally called through\n * `defineResourceLayout.lazy(...)`, which supplies shared defaults.\n */\nexport function createLazyResourceLayout<\n Resources extends ReadonlyArray<ResourceDefinition>,\n Module extends Record<string, unknown>,\n>(\n options: LazyResourceLayoutOptions<Resources, Module>,\n defaults: LazyResourceStates,\n): LazyResourceLayoutComponent<Module> {\n const runtimeOptions = options as {\n load: () => Promise<Record<string, unknown>>;\n resource: string;\n states?: LazyResourceStates;\n };\n let loadedComponentPromise: Promise<DefinedLayoutComponent> | undefined;\n const stateComponents = new Map<PropertyKey, ComponentType<any>>();\n const resolvedStates = { ...defaults, ...runtimeOptions.states };\n\n function loadComponent() {\n loadedComponentPromise ??= runtimeOptions\n .load()\n .then(findDefinedLayoutComponent)\n .catch((error: unknown) => {\n throw new ResourceModuleLoadError(runtimeOptions.resource, error);\n });\n\n return loadedComponentPromise;\n }\n\n function resetLoad() {\n loadedComponentPromise = undefined;\n stateComponents.clear();\n }\n\n function createMemberComponent(member?: PropertyKey) {\n function createLazyComponent() {\n return lazy(async () => {\n const component = await loadComponent();\n const selected =\n member === undefined\n ? component\n : (component as unknown as Record<PropertyKey, unknown>)[member];\n\n if (typeof selected !== 'function') {\n throw new LayoutConfigurationError(\n `Lazy resource \"${runtimeOptions.resource}\" does not define state \"${String(member)}\"`,\n );\n }\n\n return { default: selected as ComponentType<any> };\n });\n }\n\n function LazyMember(props: Record<string, unknown>) {\n const [, rerender] = useReducer((version) => version + 1, 0);\n const LazyComponent = useRef(createLazyComponent());\n const LoadingComponent = resolvedStates.Loading;\n const CurrentLazyComponent = LazyComponent.current;\n\n function retry() {\n resetLoad();\n LazyComponent.current = createLazyComponent();\n rerender();\n }\n\n return (\n <LazyResourceErrorBoundary\n ErrorComponent={resolvedStates.Error}\n onRetry={retry}\n >\n <Suspense\n fallback={LoadingComponent ? <LoadingComponent /> : null}\n >\n <CurrentLazyComponent {...props} />\n </Suspense>\n </LazyResourceErrorBoundary>\n );\n }\n\n const resourceName = runtimeOptions.resource;\n LazyMember.displayName =\n member === undefined\n ? `LazyResource(${resourceName})`\n : `LazyResource(${resourceName}).${String(member)}`;\n\n return LazyMember;\n }\n\n const Component = createMemberComponent();\n const unsupportedStatics = new Set<PropertyKey>([\n 'makeComposable',\n 'props',\n 'resource',\n componentPropDefinitionsSymbol,\n definedResourceLayoutComponentSymbol,\n ]);\n\n return new Proxy(Component, {\n get(target, property, receiver) {\n if (Reflect.has(target, property)) {\n return Reflect.get(target, property, receiver);\n }\n\n if (typeof property !== 'string' || unsupportedStatics.has(property)) {\n return undefined;\n }\n\n const existing = stateComponents.get(property);\n\n if (existing) {\n return existing;\n }\n\n const stateComponent = createMemberComponent(property);\n stateComponents.set(property, stateComponent);\n\n return stateComponent;\n },\n }) as unknown as LazyResourceLayoutComponent<Module>;\n}\n"],"mappings":";;;;;;;;AAwFA,IAAM,4BAAN,cAAwC,UAGtC;CACA,QAAuB,CAAC;CAExB,OAAO,yBAAyB,OAA+B;EAC7D,OAAO,EAAE,MAAM;CACjB;CAEA,cAAc;EACZ,KAAK,MAAM,QAAQ;EACnB,KAAK,SAAS,EAAE,OAAO,OAAU,CAAC;CACpC;CAEA,SAAS;EACP,IAAI,KAAK,MAAM,UAAU,QAAW;GAClC,MAAM,EAAE,mBAAmB,KAAK;GAEhC,IAAI,CAAC,gBACH,MAAM,KAAK,MAAM;GAGnB,OAAO,oBAAC,gBAAD;IAAgB,OAAO,KAAK,MAAM;IAAO,OAAO,KAAK;GAAQ;EACtE;EAEA,OAAO,KAAK,MAAM;CACpB;AACF;AAEA,SAAS,2BAA2B,QAAiC;CACnE,MAAM,aAAa,OAAO,OAAO,MAAM,EAAE,QACtC,UACC,OAAO,UAAU,cACjB,wCAAwC,KAC5C;CAEA,IAAI,WAAW,WAAW,GACxB,MAAM,IAAI,yBACR,8FAA8F,WAAW,QAC3G;CAGF,OAAO,WAAW;AACpB;;;;;;AAOA,SAAgB,yBAId,SACA,UACqC;CACrC,MAAM,iBAAiB;CAKvB,IAAI;CACJ,MAAM,kCAAkB,IAAI,IAAqC;CACjE,MAAM,iBAAiB;EAAE,GAAG;EAAU,GAAG,eAAe;CAAO;CAE/D,SAAS,gBAAgB;EACvB,2BAA2B,eACxB,KAAK,EACL,KAAK,0BAA0B,EAC/B,OAAO,UAAmB;GACzB,MAAM,IAAI,wBAAwB,eAAe,UAAU,KAAK;EAClE,CAAC;EAEH,OAAO;CACT;CAEA,SAAS,YAAY;EACnB,yBAAyB;EACzB,gBAAgB,MAAM;CACxB;CAEA,SAAS,sBAAsB,QAAsB;EACnD,SAAS,sBAAsB;GAC7B,OAAO,KAAK,YAAY;IACtB,MAAM,YAAY,MAAM,cAAc;IACtC,MAAM,WACJ,WAAW,SACP,YACC,UAAsD;IAE7D,IAAI,OAAO,aAAa,YACtB,MAAM,IAAI,yBACR,kBAAkB,eAAe,SAAS,2BAA2B,OAAO,MAAM,EAAE,EACtF;IAGF,OAAO,EAAE,SAAS,SAA+B;GACnD,CAAC;EACH;EAEA,SAAS,WAAW,OAAgC;GAClD,MAAM,GAAG,YAAY,YAAY,YAAY,UAAU,GAAG,CAAC;GAC3D,MAAM,gBAAgB,OAAO,oBAAoB,CAAC;GAClD,MAAM,mBAAmB,eAAe;GACxC,MAAM,uBAAuB,cAAc;GAE3C,SAAS,QAAQ;IACf,UAAU;IACV,cAAc,UAAU,oBAAoB;IAC5C,SAAS;GACX;GAEA,OACE,oBAAC,2BAAD;IACE,gBAAgB,eAAe;IAC/B,SAAS;cAET,oBAAC,UAAD;KACE,UAAU,mBAAmB,oBAAC,kBAAD,CAAmB,KAAI;eAEpD,oBAAC,sBAAD,EAAsB,GAAI,MAAQ;IAC1B;GACe;EAE/B;EAEA,MAAM,eAAe,eAAe;EACpC,WAAW,cACT,WAAW,SACP,gBAAgB,aAAa,KAC7B,gBAAgB,aAAa,IAAI,OAAO,MAAM;EAEpD,OAAO;CACT;CAEA,MAAM,YAAY,sBAAsB;CACxC,MAAM,qBAAqB,IAAI,IAAiB;EAC9C;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,OAAO,IAAI,MAAM,WAAW,EAC1B,IAAI,QAAQ,UAAU,UAAU;EAC9B,IAAI,QAAQ,IAAI,QAAQ,QAAQ,GAC9B,OAAO,QAAQ,IAAI,QAAQ,UAAU,QAAQ;EAG/C,IAAI,OAAO,aAAa,YAAY,mBAAmB,IAAI,QAAQ,GACjE;EAGF,MAAM,WAAW,gBAAgB,IAAI,QAAQ;EAE7C,IAAI,UACF,OAAO;EAGT,MAAM,iBAAiB,sBAAsB,QAAQ;EACrD,gBAAgB,IAAI,UAAU,cAAc;EAE5C,OAAO;CACT,EACF,CAAC;AACH"}
|