@cosmicdrift/kumiko-framework 0.362.0 → 0.364.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.
Files changed (259) hide show
  1. package/AGENTS.md +307 -0
  2. package/agents/examples/access-control.md +355 -0
  3. package/agents/examples/anonymous-access-multitenant.md +443 -0
  4. package/agents/examples/anonymous-access.md +300 -0
  5. package/agents/examples/apex-landing.md +528 -0
  6. package/agents/examples/apex-surface-auth.md +355 -0
  7. package/agents/examples/api-worker-split.md +434 -0
  8. package/agents/examples/auth-claims.md +351 -0
  9. package/agents/examples/auth-foundation-providers.md +155 -0
  10. package/agents/examples/auth-mfa.md +323 -0
  11. package/agents/examples/basic-entity.md +420 -0
  12. package/agents/examples/boot-check.md +122 -0
  13. package/agents/examples/chat-channels.md +254 -0
  14. package/agents/examples/compliance-profiles-demo.md +307 -0
  15. package/agents/examples/cross-feature-events.md +317 -0
  16. package/agents/examples/crypto-shredding-hr.md +314 -0
  17. package/agents/examples/currencies-global.md +609 -0
  18. package/agents/examples/currencies-per-tenant.md +453 -0
  19. package/agents/examples/custom-fields-basic.md +314 -0
  20. package/agents/examples/custom-handlers.md +397 -0
  21. package/agents/examples/delivery-notifications.md +432 -0
  22. package/agents/examples/designer-demo.md +86 -0
  23. package/agents/examples/e2e-generator.md +211 -0
  24. package/agents/examples/embedded-entity-form.md +598 -0
  25. package/agents/examples/embedded.md +649 -0
  26. package/agents/examples/encrypted-tenant-config.md +405 -0
  27. package/agents/examples/error-contract.md +539 -0
  28. package/agents/examples/event-sourcing.md +1287 -0
  29. package/agents/examples/feature-toggles.md +263 -0
  30. package/agents/examples/field-access.md +271 -0
  31. package/agents/examples/files-post-processing.md +433 -0
  32. package/agents/examples/i18n.md +166 -0
  33. package/agents/examples/idempotency.md +179 -0
  34. package/agents/examples/inbound-mail-triage.md +315 -0
  35. package/agents/examples/lane-routing.md +246 -0
  36. package/agents/examples/ledger-banking.md +214 -0
  37. package/agents/examples/ledger-invoicing.md +235 -0
  38. package/agents/examples/legal-pages.md +393 -0
  39. package/agents/examples/lifecycle-hooks.md +368 -0
  40. package/agents/examples/managed-config.md +428 -0
  41. package/agents/examples/managed-pages.md +353 -0
  42. package/agents/examples/notes-history-basic.md +345 -0
  43. package/agents/examples/ownership.md +396 -0
  44. package/agents/examples/pdf-audit-export.md +270 -0
  45. package/agents/examples/pipeline-basics.md +792 -0
  46. package/agents/examples/public-share-token.md +351 -0
  47. package/agents/examples/rate-limiting.md +211 -0
  48. package/agents/examples/realtime-sse.md +187 -0
  49. package/agents/examples/record-detail-layout.md +297 -0
  50. package/agents/examples/reference-data.md +151 -0
  51. package/agents/examples/relations.md +280 -0
  52. package/agents/examples/screens-nav.md +353 -0
  53. package/agents/examples/search.md +233 -0
  54. package/agents/examples/secrets-demo.md +417 -0
  55. package/agents/examples/security-baseline.md +187 -0
  56. package/agents/examples/seed-migration.md +99 -0
  57. package/agents/examples/session-revocation.md +259 -0
  58. package/agents/examples/state-machine.md +920 -0
  59. package/agents/examples/tags-basic.md +401 -0
  60. package/agents/examples/tenant-isolation.md +173 -0
  61. package/agents/examples/tenant-settings.md +275 -0
  62. package/agents/examples/tier-admin.md +347 -0
  63. package/agents/examples/timezones.md +366 -0
  64. package/agents/examples/user-data-rights.md +410 -0
  65. package/agents/examples/user-profile.md +226 -0
  66. package/agents/examples/webhook-step.md +597 -0
  67. package/agents/examples/wizard-form.md +379 -0
  68. package/agents/examples/workflow-engine.md +1731 -0
  69. package/agents/examples/writeform-section.md +300 -0
  70. package/agents/examples.md +74 -0
  71. package/agents/features.md +768 -0
  72. package/agents/widgets.md +181 -0
  73. package/dist/api/auth-middleware.d.ts +2 -0
  74. package/dist/api/auth-middleware.js +14 -5
  75. package/dist/api/auth-routes.js +5 -3
  76. package/dist/api/extra-route.d.ts +3 -0
  77. package/dist/api/index.d.ts +2 -2
  78. package/dist/api/index.js +1 -1
  79. package/dist/api/redis-sse-broker.js +10 -8
  80. package/dist/api/routes.js +48 -19
  81. package/dist/api/server.d.ts +2 -1
  82. package/dist/api/server.js +18 -6
  83. package/dist/api/session-cookies.d.ts +1 -0
  84. package/dist/api/session-cookies.js +20 -5
  85. package/dist/api/sse-route.js +16 -1
  86. package/dist/bun-db/index.d.ts +1 -1
  87. package/dist/bun-db/index.js +1 -1
  88. package/dist/bun-db/query.d.ts +12 -5
  89. package/dist/bun-db/query.js +88 -94
  90. package/dist/crypto/kek-source.js +5 -3
  91. package/dist/crypto/pii-field-encryption.d.ts +4 -1
  92. package/dist/crypto/pii-field-encryption.js +2 -2
  93. package/dist/db/apply-entity-event.js +2 -2
  94. package/dist/db/collect-table-metas.js +11 -7
  95. package/dist/db/cursor.js +2 -1
  96. package/dist/db/dialect.d.ts +1 -0
  97. package/dist/db/dialect.js +28 -13
  98. package/dist/db/eagerload.js +2 -2
  99. package/dist/db/entity-table-from-registry.js +1 -1
  100. package/dist/db/entity-table-meta.js +5 -13
  101. package/dist/db/event-store-executor-context.d.ts +5 -6
  102. package/dist/db/event-store-executor-context.js +29 -27
  103. package/dist/db/event-store-executor-read.js +40 -25
  104. package/dist/db/event-store-executor-write.js +18 -20
  105. package/dist/db/index.d.ts +1 -1
  106. package/dist/db/index.js +1 -1
  107. package/dist/db/lock-guard.d.ts +4 -0
  108. package/dist/db/lock-guard.js +14 -0
  109. package/dist/db/migrate-generator.js +6 -4
  110. package/dist/db/migrate-runner.js +32 -35
  111. package/dist/db/parent-ref-clause.js +2 -2
  112. package/dist/db/queries/backfill-pii.js +11 -9
  113. package/dist/db/queries/es-ops.js +1 -1
  114. package/dist/db/queries/event-consumer.js +17 -17
  115. package/dist/db/queries/event-store-admin.js +6 -6
  116. package/dist/db/queries/event-store-transfer.js +2 -2
  117. package/dist/db/queries/event-store.js +2 -2
  118. package/dist/db/queries/pii-backfill-state.js +2 -2
  119. package/dist/db/queries/raw-sql.js +1 -1
  120. package/dist/db/queries/test-stack.js +1 -1
  121. package/dist/db/reference-data.js +3 -5
  122. package/dist/db/table-builder.js +6 -4
  123. package/dist/db/table-introspection.d.ts +16 -0
  124. package/dist/db/table-introspection.js +76 -0
  125. package/dist/db/tenant-db-runner.d.ts +4 -0
  126. package/dist/db/tenant-db-runner.js +17 -0
  127. package/dist/db/tenant-db.d.ts +5 -2
  128. package/dist/db/tenant-db.js +34 -14
  129. package/dist/derivatives/derivatives-context.js +1 -1
  130. package/dist/derivatives/variant-key.js +2 -1
  131. package/dist/engine/boot-validator/access-declarations.js +2 -2
  132. package/dist/engine/boot-validator/api-ext.js +3 -3
  133. package/dist/engine/boot-validator/boot-check.js +1 -1
  134. package/dist/engine/boot-validator/config-deps.js +13 -13
  135. package/dist/engine/boot-validator/detail-screens.js +1 -1
  136. package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
  137. package/dist/engine/boot-validator/entity-handler.js +84 -35
  138. package/dist/engine/boot-validator/entity-list-screens.js +3 -3
  139. package/dist/engine/boot-validator/handler-field-references.js +19 -9
  140. package/dist/engine/boot-validator/i18n-keys.js +2 -2
  141. package/dist/engine/boot-validator/nav.js +1 -1
  142. package/dist/engine/boot-validator/pii-retention.js +9 -9
  143. package/dist/engine/boot-validator/projection-list-screens.js +5 -5
  144. package/dist/engine/boot-validator/query-output-columns.js +7 -7
  145. package/dist/engine/boot-validator/screens.d.ts +2 -1
  146. package/dist/engine/boot-validator/screens.js +155 -96
  147. package/dist/engine/boot-validator/url-prefill-fields.js +54 -35
  148. package/dist/engine/changeset-changes.js +3 -3
  149. package/dist/engine/define-handler.d.ts +2 -2
  150. package/dist/engine/define-handler.js +11 -7
  151. package/dist/engine/embedded-derived.js +10 -6
  152. package/dist/engine/extensions/escape-hatch-usage.js +2 -1
  153. package/dist/engine/factories.js +5 -6
  154. package/dist/engine/feature-ast/extractors/events.js +43 -33
  155. package/dist/engine/feature-ast/extractors/handlers.js +2 -0
  156. package/dist/engine/feature-ast/patcher.d.ts +3 -3
  157. package/dist/engine/feature-ast/patcher.js +3 -3
  158. package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -8
  159. package/dist/engine/feature-ast/pattern-change-schema.js +4 -3
  160. package/dist/engine/feature-ast/patterns.d.ts +3 -3
  161. package/dist/engine/feature-ast/render.js +16 -5
  162. package/dist/engine/feature-changelog.d.ts +2 -1
  163. package/dist/engine/feature-changelog.js +8 -6
  164. package/dist/engine/feature-config-events-jobs.d.ts +17 -7
  165. package/dist/engine/feature-config-events-jobs.js +20 -5
  166. package/dist/engine/feature-entity-handlers.d.ts +2 -2
  167. package/dist/engine/feature-entity-handlers.js +12 -3
  168. package/dist/engine/feature-ui-extensions.js +2 -3
  169. package/dist/engine/handler-field-references.d.ts +21 -2
  170. package/dist/engine/handler-field-references.js +55 -17
  171. package/dist/engine/index.d.ts +3 -2
  172. package/dist/engine/index.js +1 -0
  173. package/dist/engine/ownership.js +7 -20
  174. package/dist/engine/pattern-library/mixed-schemas.js +0 -1
  175. package/dist/engine/registry-ingest.d.ts +2 -1
  176. package/dist/engine/registry-ingest.js +85 -2
  177. package/dist/engine/registry-validate.d.ts +1 -0
  178. package/dist/engine/registry-validate.js +38 -8
  179. package/dist/engine/registry.js +2 -1
  180. package/dist/engine/schema-builder.js +3 -4
  181. package/dist/engine/soft-delete-cleanup.js +12 -4
  182. package/dist/engine/steps/call-feature.js +1 -3
  183. package/dist/engine/steps/retry.js +2 -1
  184. package/dist/engine/steps/unsafe-projection-delete.js +2 -2
  185. package/dist/engine/steps/unsafe-projection-upsert.js +4 -15
  186. package/dist/engine/template-variables.d.ts +32 -0
  187. package/dist/engine/template-variables.js +80 -0
  188. package/dist/engine/tier-resolver-extension.js +2 -3
  189. package/dist/engine/types/index.d.ts +2 -2
  190. package/dist/engine/validate-projection-allowlist.js +12 -13
  191. package/dist/engine/where-rule-lint.js +8 -7
  192. package/dist/env/_zod-introspect.d.ts +1 -0
  193. package/dist/errors/classes.js +1 -0
  194. package/dist/errors/kumiko-error.d.ts +2 -0
  195. package/dist/errors/kumiko-error.js +2 -0
  196. package/dist/errors/reasons.d.ts +1 -0
  197. package/dist/errors/reasons.js +2 -0
  198. package/dist/errors/write-error-info.js +2 -0
  199. package/dist/event-store/tenant-db-append.js +2 -2
  200. package/dist/files/assert-file-refs-belong-to-tenant.d.ts +4 -5
  201. package/dist/files/assert-file-refs-belong-to-tenant.js +7 -15
  202. package/dist/files/bind-pending-file-refs.d.ts +2 -0
  203. package/dist/files/bind-pending-file-refs.js +3 -3
  204. package/dist/files/bind-referenced-files.write.d.ts +1 -1
  205. package/dist/files/bind-referenced-files.write.js +22 -13
  206. package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
  207. package/dist/files/create-file-ref-from-bytes.js +67 -4
  208. package/dist/files/create-file-ref.d.ts +22 -0
  209. package/dist/files/create-file-ref.js +53 -25
  210. package/dist/files/file-handle.d.ts +1 -1
  211. package/dist/files/file-handle.js +8 -0
  212. package/dist/files/file-routes.js +2 -2
  213. package/dist/files/index.d.ts +2 -2
  214. package/dist/files/index.js +1 -1
  215. package/dist/files/load-save-file-refs.d.ts +10 -0
  216. package/dist/files/load-save-file-refs.js +47 -0
  217. package/dist/files/local-provider.js +2 -1
  218. package/dist/files/purge-expired-file-ref-bytes.d.ts +4 -1
  219. package/dist/files/purge-expired-file-ref-bytes.js +45 -19
  220. package/dist/i18n/index.d.ts +1 -1
  221. package/dist/i18n/index.js +1 -1
  222. package/dist/i18n/mail-registry.d.ts +2 -0
  223. package/dist/i18n/mail-registry.js +7 -0
  224. package/dist/jobs/index.d.ts +1 -1
  225. package/dist/jobs/job-runner.d.ts +4 -1
  226. package/dist/jobs/job-runner.js +13 -9
  227. package/dist/observability/escape-hatch-report.d.ts +1 -1
  228. package/dist/observability/escape-hatch-report.js +17 -5
  229. package/dist/pipeline/cascade-handler.js +5 -2
  230. package/dist/pipeline/dispatch-batch.js +79 -31
  231. package/dist/pipeline/dispatch-query.js +2 -3
  232. package/dist/pipeline/dispatch-shared.d.ts +7 -2
  233. package/dist/pipeline/dispatch-shared.js +142 -34
  234. package/dist/pipeline/dispatch-write.js +34 -22
  235. package/dist/pipeline/event-dispatcher-delivery.js +2 -3
  236. package/dist/pipeline/index.d.ts +2 -0
  237. package/dist/pipeline/index.js +2 -0
  238. package/dist/pipeline/member-read-only-transaction.js +1 -1
  239. package/dist/pipeline/notify-scope.d.ts +13 -0
  240. package/dist/pipeline/notify-scope.js +32 -0
  241. package/dist/pipeline/pre-transaction-reservations.d.ts +3 -1
  242. package/dist/pipeline/pre-transaction-reservations.js +28 -9
  243. package/dist/pipeline/system-hooks.js +2 -1
  244. package/dist/pipeline/system-identity-switch.js +2 -2
  245. package/dist/rate-limit/bucket.d.ts +3 -3
  246. package/dist/rate-limit/bucket.js +14 -4
  247. package/dist/redis/cache-sync-bus.js +9 -3
  248. package/dist/search/meilisearch-adapter.js +39 -13
  249. package/dist/secrets/stored-envelope.js +7 -7
  250. package/dist/stack/push-entity-projection-tables.js +2 -3
  251. package/dist/stack/table-helpers.js +10 -26
  252. package/dist/stack/test-stack.js +2 -5
  253. package/dist/testing/multipart-helper.d.ts +0 -6
  254. package/dist/testing/multipart-helper.js +5 -5
  255. package/dist/testing/without-ambient-temporal.js +4 -4
  256. package/dist/utils/is-plain-object.d.ts +1 -2
  257. package/dist/utils/is-plain-object.js +1 -4
  258. package/package.json +7 -5
  259. package/src/changes.json +251 -8
@@ -0,0 +1,211 @@
1
+ <!-- Generated by `bun run gen:agent-docs` from samples/recipes/e2e-generator. Do not edit. -->
2
+
3
+ # e2e-generator
4
+
5
+ generateE2ESpec + withBootValidatorFixture — Registry → JSON-Specs für externen Playwright-Worker. Zeigt die 4 Test-Kinds, Feldtyp→Interaktion-Mapping und welche Feldtypen heute noch nicht generisch unterstützt sind.
6
+
7
+ ## README
8
+
9
+ # E2E generator
10
+
11
+ Generate Playwright E2E specs from the registry.
12
+
13
+ ## What it shows
14
+
15
+ - `generateE2ESpec` (+ `withBootValidatorFixture` in the test) → JSON specs for an external Playwright worker
16
+ - The four test kinds and field-type → interaction mapping
17
+
18
+ ## Source
19
+
20
+ Feature entry point: `src/feature.ts`.
21
+
22
+ ## Tests
23
+
24
+ ```bash
25
+ cd samples/recipes/e2e-generator
26
+ bun test
27
+ ```
28
+
29
+ ## Source files
30
+
31
+ ### src/__tests__/feature.test.ts
32
+
33
+ ```ts
34
+ // E2E-Generator Sample — Unit-Test.
35
+ //
36
+ // Zweck: zeigt einen realistischen Generator-Run (shop-Feature mit allen
37
+ // unterstützten + ein paar bewusst unsupported Feldtypen). Der Snapshot
38
+ // dokumentiert das Ergebnis; jeder Feature-Autor der den Generator zum
39
+ // ersten Mal anfasst kann hier reinschauen statt in die Implementation.
40
+
41
+ import { describe, expect, test } from "bun:test";
42
+ import {
43
+ createRegistry,
44
+ validateBoot as validateBootRaw,
45
+ } from "@cosmicdrift/kumiko-framework/engine";
46
+ import { generateE2ESpec, withBootValidatorFixture } from "@cosmicdrift/kumiko-framework/testing";
47
+ import { createShopFeature } from "../feature";
48
+
49
+ function validateBoot(features: Parameters<typeof validateBootRaw>[0]): void {
50
+ validateBootRaw(withBootValidatorFixture(features));
51
+ }
52
+
53
+ const shop = createShopFeature();
54
+ const registry = createRegistry([shop]);
55
+
56
+ describe("e2e-generator sample — shop feature", () => {
57
+ test("validateBoot akzeptiert das Feature", () => {
58
+ expect(() => validateBoot([shop])).not.toThrow();
59
+ });
60
+
61
+ test("generateE2ESpec liefert alle vier Kinds für das List+Edit-Paar", () => {
62
+ const specs = generateE2ESpec(registry);
63
+ const kinds = specs.map((s) => s.kind).sort();
64
+ expect(kinds).toEqual([
65
+ "edit-save-persists",
66
+ "edit-validates-required",
67
+ "list-has-fixture-row",
68
+ "list-renders",
69
+ ]);
70
+ });
71
+
72
+ test("list-has-fixture-row referenziert den Create-Handler + nutzt name als Identifying", () => {
73
+ const specs = generateE2ESpec(registry);
74
+ const row = specs.find((s) => s.kind === "list-has-fixture-row");
75
+ if (row?.kind !== "list-has-fixture-row") throw new Error("unreachable");
76
+
77
+ expect(row.writeHandlerQn).toBe("shop:write:product:create");
78
+ expect(row.urlPath).toBe("/t/{tenant}/shop/product-list");
79
+ // name ist die erste Text-Column → Identifying-Value
80
+ expect(row.identifyingValue).toBe("e2e name");
81
+ // money hat eine generische Object-Fixture → landet im API-Seed (die API
82
+ // erwartet { amount, currency }), aber NICHT im Edit-Form (siehe
83
+ // edit-save-persists-Test unten — kein Form-Widget generisch).
84
+ expect(row.fixture["listPrice"]).toEqual({ amount: 1, currency: "EUR" });
85
+ });
86
+
87
+ test("edit-validates-required listet nur echte required-Felder (name, price)", () => {
88
+ const specs = generateE2ESpec(registry);
89
+ const validates = specs.find((s) => s.kind === "edit-validates-required");
90
+ if (validates?.kind !== "edit-validates-required") throw new Error("unreachable");
91
+ expect(validates.requiredFields).toEqual(["name", "price"]);
92
+ });
93
+
94
+ test("edit-save-persists mapped Feldtypen auf die richtigen Playwright-Interaktionen", () => {
95
+ const specs = generateE2ESpec(registry);
96
+ const save = specs.find((s) => s.kind === "edit-save-persists");
97
+ if (save?.kind !== "edit-save-persists") throw new Error("unreachable");
98
+
99
+ const opsByField = new Map(save.fills.map((op) => [op.field, op.kind]));
100
+ expect(opsByField.get("name")).toBe("fill");
101
+ expect(opsByField.get("description")).toBe("fill");
102
+ expect(opsByField.get("price")).toBe("fill");
103
+ // DER eigentliche Regression-Guard: select darf NICHT auf .fill() landen,
104
+ // sonst zerschellt Playwright am ersten Dropdown.
105
+ expect(opsByField.get("status")).toBe("select");
106
+ expect(opsByField.get("featured")).toBe("check");
107
+ // money (listPrice) wird im Form bewusst übersprungen — komplexes
108
+ // Widget (Betrag + Währung), das der Generator nicht generisch
109
+ // automatisieren kann. API-Seed nutzt es trotzdem (siehe Test oben).
110
+ expect(opsByField.has("listPrice")).toBe(false);
111
+ });
112
+
113
+ test("Output ist JSON-serialisierbar (Prozess-Grenze zu Playwright-Worker)", () => {
114
+ // Das Sample-Feature muss sich rundtrippen lassen: TestSpec → JSON.stringify →
115
+ // JSON.parse. Im produktiven Setup schreibt ein bun-Subprozess (globalSetup)
116
+ // die JSON auf Platte, der Playwright-Worker liest sie — kein runtime-Import
117
+ // vom Framework im Test-Prozess (würde mit Playwrights expect kollidieren).
118
+ const specs = generateE2ESpec(registry);
119
+ const roundtripped = JSON.parse(JSON.stringify(specs));
120
+ expect(roundtripped).toEqual(specs);
121
+ });
122
+ });
123
+ ```
124
+
125
+ ### src/feature.ts
126
+
127
+ ```ts
128
+ import {
129
+ createBooleanField,
130
+ createEntity,
131
+ createMoneyField,
132
+ createNumberField,
133
+ createSelectField,
134
+ createTextField,
135
+ defineFeature,
136
+ type FeatureDefinition,
137
+ } from "@cosmicdrift/kumiko-framework/engine";
138
+
139
+ export const productEntity = createEntity({
140
+ table: "read_products",
141
+ fields: {
142
+ name: createTextField({
143
+ personal: false,
144
+ reason: "is_business_data",
145
+ required: true,
146
+ maxLength: 200,
147
+ }),
148
+ description: createTextField({ maxLength: 2000, personal: false, reason: "is_business_data" }),
149
+ price: createNumberField({ required: true }),
150
+ status: createSelectField({ options: ["draft", "published", "archived"] as const }),
151
+ featured: createBooleanField({ default: false }),
152
+ listPrice: createMoneyField({}),
153
+ },
154
+ });
155
+
156
+ const editorWrite = { access: { roles: ["Admin", "Editor"] } } as const;
157
+
158
+ export function createShopFeature(): FeatureDefinition {
159
+ return defineFeature("shop", (r) => {
160
+ r.systemScope();
161
+ r.crud("product", productEntity, {
162
+ write: editorWrite,
163
+ verbs: { list: false, detail: false, restore: false },
164
+ });
165
+
166
+ r.screen({
167
+ id: "product-list",
168
+ type: "entityList",
169
+ entity: "product",
170
+ columns: [
171
+ "name",
172
+ { field: "price", renderer: { format: "currency", symbol: "€" } },
173
+ "status",
174
+ "featured",
175
+ ],
176
+ });
177
+
178
+ r.screen({
179
+ id: "product-edit",
180
+ type: "entityEdit",
181
+ entity: "product",
182
+ layout: {
183
+ sections: [
184
+ {
185
+ title: "shop:section.basics",
186
+ columns: 2,
187
+ fields: ["name", { field: "description", span: 2 }],
188
+ },
189
+ {
190
+ title: "shop:section.publishing",
191
+ columns: 2,
192
+ fields: ["price", "status", "featured"],
193
+ },
194
+ {
195
+ title: "shop:section.pricing",
196
+ fields: ["listPrice"],
197
+ },
198
+ ],
199
+ },
200
+ });
201
+
202
+ r.nav({
203
+ id: "products",
204
+ label: "shop:nav.products",
205
+ icon: "package",
206
+ screen: "shop:screen:product-list",
207
+ order: 10,
208
+ });
209
+ });
210
+ }
211
+ ```