@wocart/theme 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ All notable changes to `@wocart/theme` are recorded here. The package follows
5
5
  major version, and an export is marked `@deprecated` for at least one minor
6
6
  release before a major removes it.
7
7
 
8
+ ## 1.3.0 — 2026-10-17
9
+
10
+ ### Added
11
+
12
+ - `load` is typed from the component's own fields: `defineComponent` hands it
13
+ `props` as `FieldValues<fields>` (a field with a default is always there,
14
+ any other may be absent), so a definition reads its settings — which
15
+ collection, how many products — without a cast. What `load` returns still
16
+ types the component's props (`InferProps`). Type `FieldValues`.
17
+
8
18
  ## 1.2.0 — 2026-10-17
9
19
 
10
20
  ### Added
package/README.md CHANGED
@@ -89,6 +89,24 @@ props and undeclared children/slots, is reported through `onIssue` (default:
89
89
  `console.warn`). The issue contains a stable `code`, `message`, `instanceId`,
90
90
  and, for page-data resolution issues, `path`.
91
91
 
92
+ `load` receives the instance's resolved props typed from the component's own
93
+ fields, so a section reads its settings without a cast:
94
+
95
+ ```ts
96
+ defineComponent({
97
+ type: "best-sellers",
98
+ component: BestSellers,
99
+ label: { ar: "الأكثر مبيعاً", fr: "Meilleures ventes" },
100
+ fields: [
101
+ { key: "collection", kind: "collection", label: { ar: "المجموعة", fr: "Collection" }, default: "best-sellers" },
102
+ { key: "limit", kind: "number", label: { ar: "العدد", fr: "Nombre" }, default: 8, min: 1, max: 24 },
103
+ ],
104
+ load: async ({ storefront, props }) => ({
105
+ products: await storefront.products.list({ collection: props.collection, limit: props.limit }),
106
+ }),
107
+ });
108
+ ```
109
+
92
110
  ## Field kinds
93
111
 
94
112
  | Kind | Value | Rules |
@@ -1,7 +1,27 @@
1
- import type { ComponentDefinition, ThemeDefinition, ThemeDefinitionIssue, ThemeValidationOptions } from "./types.js";
2
- /** Identity helper: returns the definition unchanged so literal types survive. */
3
- export declare function defineComponent<const T extends ComponentDefinition>(definition: T): T;
1
+ import type { ComponentDefinition, FieldDefinition, FieldValues, LoadContext, ThemeDefinition, ThemeDefinitionIssue, ThemeValidationOptions } from "./types.js";
2
+ /**
3
+ * Identity helper: returns the definition unchanged so literal types survive.
4
+ * `load` receives the component's props typed from its own `fields`, so a
5
+ * definition reads its settings without a cast, and what it returns types the
6
+ * component's props (`InferProps`).
7
+ */
8
+ export declare function defineComponent<const T extends ComponentBase, Loaded extends Record<string, unknown>, const Fields extends readonly FieldDefinition[] = readonly []>(definition: T & {
9
+ fields?: Fields;
10
+ load: (context: LoadContext<FieldValues<Fields>>) => Promise<Loaded>;
11
+ }): T & FieldsPart<Fields> & {
12
+ load: (context: LoadContext) => Promise<Loaded>;
13
+ };
14
+ export declare function defineComponent<const T extends ComponentBase, const Fields extends readonly FieldDefinition[] = readonly []>(definition: T & {
15
+ fields?: Fields;
16
+ load?: undefined;
17
+ }): T & FieldsPart<Fields>;
18
+ type ComponentBase = Omit<ComponentDefinition, "fields" | "load">;
19
+ /** The field literals a definition declared, when it declared any. */
20
+ type FieldsPart<Fields extends readonly FieldDefinition[]> = [Fields] extends [readonly []] ? {} : {
21
+ fields: Fields;
22
+ };
4
23
  /** Identity helper for the catalog. */
5
24
  export declare function defineTheme<const T extends ThemeDefinition>(definition: T): T;
6
25
  /** Check the whole catalog: key/type identity, field rules, slot rules. */
7
26
  export declare function validateThemeDefinition(theme: ThemeDefinition, options?: ThemeValidationOptions): ThemeDefinitionIssue[];
27
+ export {};
@@ -5,7 +5,6 @@
5
5
  * bugs when the catalog is assembled.
6
6
  */
7
7
  import { COLOR_PATTERN, MENU_HANDLE_PATTERN, COLLECTION_HANDLE_PATTERN, SURFACE_KEY_PATTERN, THEME_KEY_PATTERN, TYPE_KEY_PATTERN, isLocaleMap, isLocaleTag, isSafeLinkTarget, } from "./schemas.js";
8
- /** Identity helper: returns the definition unchanged so literal types survive. */
9
8
  export function defineComponent(definition) {
10
9
  return definition;
11
10
  }
package/dist/index.d.ts CHANGED
@@ -17,4 +17,4 @@ export { localize } from "./locales.js";
17
17
  export { PLURAL_CATEGORIES, formatMessage, messageForms, pluralFormIssues } from "./messages.js";
18
18
  export type { MessageOptions, MessageValues, PluralCategory } from "./messages.js";
19
19
  export { THEME_MANIFEST_CONTRACT_VERSION, themeManifest, validateThemeManifest, } from "./manifest.js";
20
- export type { JsonValue, JsonObject, LocaleCode, LocalizedText, ResolutionScope, FieldKind, FieldDefinition, FieldValue, InferProps, SlotDefinition, ComponentCapabilities, ComponentDefinition, ComponentSpec, SurfaceSpec, ThemeDefinition, SettingsGroup, InferSettings, ThemeSettingsIssue, ThemeSettingsIssueCode, ThemeSettingsResult, ColorFieldDefinition, MenuFieldDefinition, CollectionFieldDefinition, ThemeManifest, ThemeManifestIssue, ThemeManifestIssueCode, ThemeManifestResult, ComponentInstance, PageData, ThemeRenderContext, LoadContext, LoadFn, ReadCache, PageDataIssue, PageDataIssueCode, PageDataResult, ResolvedInstance, ThemeDefinitionIssue, ThemeDefinitionIssueCode, ThemeValidationOptions, } from "./types.js";
20
+ export type { JsonValue, JsonObject, LocaleCode, LocalizedText, ResolutionScope, FieldKind, FieldDefinition, FieldValue, InferProps, FieldValues, SlotDefinition, ComponentCapabilities, ComponentDefinition, ComponentSpec, SurfaceSpec, ThemeDefinition, SettingsGroup, InferSettings, ThemeSettingsIssue, ThemeSettingsIssueCode, ThemeSettingsResult, ColorFieldDefinition, MenuFieldDefinition, CollectionFieldDefinition, ThemeManifest, ThemeManifestIssue, ThemeManifestIssueCode, ThemeManifestResult, ComponentInstance, PageData, ThemeRenderContext, LoadContext, LoadFn, ReadCache, PageDataIssue, PageDataIssueCode, PageDataResult, ResolvedInstance, ThemeDefinitionIssue, ThemeDefinitionIssueCode, ThemeValidationOptions, } from "./types.js";
package/dist/types.d.ts CHANGED
@@ -204,7 +204,7 @@ export interface ReadCache {
204
204
  get<T>(key: string, load: () => T | Promise<T>): Promise<T>;
205
205
  }
206
206
  /** What a `load` hook receives. Server-only; reads go through the storefront SDK. */
207
- export interface LoadContext {
207
+ export interface LoadContext<P = Record<string, unknown>> {
208
208
  storefront: StoreClient;
209
209
  context: ThemeRenderContext;
210
210
  /**
@@ -215,11 +215,16 @@ export interface LoadContext {
215
215
  * the same name. A definition whose component reads a prop from `load` should
216
216
  * keep that name out of `fields`.
217
217
  */
218
- props: Record<string, unknown>;
218
+ props: P;
219
219
  reads: ReadCache;
220
220
  }
221
- /** Server-side data fetch for one component definition. */
222
- export type LoadFn = (context: LoadContext) => Promise<Record<string, unknown>>;
221
+ /**
222
+ * Server-side data fetch for one component definition. Through
223
+ * `defineComponent`, `props` is typed from the definition's own fields
224
+ * (`FieldValues`): a field with a default is always there, any other may be
225
+ * absent.
226
+ */
227
+ export type LoadFn<P = Record<string, unknown>> = (context: LoadContext<P>) => Promise<Record<string, unknown>>;
223
228
  /**
224
229
  * Code-owned description of one reusable component. `component` is the real
225
230
  * component reference — opaque here so the core stays framework-neutral; the
@@ -249,6 +254,13 @@ export type FieldValue<F extends FieldDefinition> = F extends {
249
254
  type FieldsOf<D> = D extends {
250
255
  fields: readonly (infer F extends FieldDefinition)[];
251
256
  } ? F : never;
257
+ /**
258
+ * The resolved values of a list of fields, as `load` and the component receive
259
+ * them: a field with a `default` is always present, any other may be absent.
260
+ */
261
+ export type FieldValues<Fields extends readonly FieldDefinition[]> = FieldProps<{
262
+ fields: Fields;
263
+ }>;
252
264
  type FieldProps<D> = {
253
265
  [F in FieldsOf<D> as F extends {
254
266
  default: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wocart/theme",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "The Wocart theme contract: code-owned component definitions, JSON page documents, validation and resolution, and a reference Astro renderer.",
5
5
  "keywords": [
6
6
  "wocart",