@flowselections/floriday-voorraad 1.0.13 → 1.0.15

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 (114) hide show
  1. package/dist-lib/assets/floriday-logo.png.asset.json +11 -0
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +5 -0
  3. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -0
  4. package/dist-lib/components/voorraad/ArtikelWizard.js +1229 -0
  5. package/dist-lib/components/voorraad/EditArtikelPage.d.ts +4 -0
  6. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -0
  7. package/dist-lib/components/voorraad/EditArtikelPage.js +51 -0
  8. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts +2 -0
  9. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -0
  10. package/dist-lib/components/voorraad/NieuwArtikelPage.js +5 -0
  11. package/dist-lib/components/voorraad/StockBulkButtons.d.ts +7 -0
  12. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -0
  13. package/dist-lib/components/voorraad/StockBulkButtons.js +356 -0
  14. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +7 -0
  15. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -0
  16. package/dist-lib/components/voorraad/ValidationOverviewTable.js +87 -0
  17. package/dist-lib/components/voorraad/ValidationPage.d.ts +2 -0
  18. package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -0
  19. package/dist-lib/components/voorraad/ValidationPage.js +12 -0
  20. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts +6 -0
  21. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -0
  22. package/dist-lib/components/voorraad/VoorraadDashboard.js +42 -0
  23. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  24. package/dist-lib/components/voorraad/VoorraadPage.js +70 -11
  25. package/dist-lib/components/voorraad/VoorraadPagination.d.ts +10 -0
  26. package/dist-lib/components/voorraad/VoorraadPagination.d.ts.map +1 -0
  27. package/dist-lib/components/voorraad/VoorraadPagination.js +12 -0
  28. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +8 -0
  29. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -0
  30. package/dist-lib/components/voorraad/VoorraadStockCard.js +33 -0
  31. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  32. package/dist-lib/components/voorraad/VoorraadTable.js +16 -5
  33. package/dist-lib/hooks/useFloriday.d.ts +55 -0
  34. package/dist-lib/hooks/useFloriday.d.ts.map +1 -0
  35. package/dist-lib/hooks/useFloriday.js +298 -0
  36. package/dist-lib/hooks/useLocalTradeItems.d.ts +17 -1
  37. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  38. package/dist-lib/hooks/useLocalTradeItems.js +54 -10
  39. package/dist-lib/hooks/useTradeItemSpec.d.ts +42 -0
  40. package/dist-lib/hooks/useTradeItemSpec.d.ts.map +1 -0
  41. package/dist-lib/hooks/useTradeItemSpec.js +448 -0
  42. package/dist-lib/hooks/useTradeItemStock.d.ts +19 -0
  43. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -0
  44. package/dist-lib/hooks/useTradeItemStock.js +78 -0
  45. package/dist-lib/hooks/useVbnRegulatoryFeatures.d.ts +3 -0
  46. package/dist-lib/hooks/useVbnRegulatoryFeatures.d.ts.map +1 -0
  47. package/dist-lib/hooks/useVbnRegulatoryFeatures.js +19 -0
  48. package/dist-lib/hooks/useVoorraadData.d.ts +2 -1
  49. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  50. package/dist-lib/hooks/useVoorraadData.js +39 -11
  51. package/dist-lib/index.d.ts +4 -0
  52. package/dist-lib/index.d.ts.map +1 -1
  53. package/dist-lib/index.js +6 -5
  54. package/dist-lib/integrations/supabase/auth-middleware.d.ts +8346 -3367
  55. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  56. package/dist-lib/integrations/supabase/client.d.ts +8346 -3367
  57. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  58. package/dist-lib/integrations/supabase/client.server.d.ts +8346 -3367
  59. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  60. package/dist-lib/integrations/supabase/types.d.ts +8460 -3341
  61. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  62. package/dist-lib/integrations/supabase/types.js +10 -1
  63. package/dist-lib/lib/__tests__/floricode-required-features.test.d.ts +2 -0
  64. package/dist-lib/lib/__tests__/floricode-required-features.test.d.ts.map +1 -0
  65. package/dist-lib/lib/__tests__/floricode-required-features.test.js +186 -0
  66. package/dist-lib/lib/floricode-client.d.ts +15 -1
  67. package/dist-lib/lib/floricode-client.d.ts.map +1 -1
  68. package/dist-lib/lib/floricode-client.js +42 -4
  69. package/dist-lib/lib/floricode-required-features.functions.d.ts +10026 -0
  70. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -0
  71. package/dist-lib/lib/floricode-required-features.functions.js +170 -0
  72. package/dist-lib/lib/floriday-client.d.ts +36 -8
  73. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  74. package/dist-lib/lib/floriday-client.js +206 -25
  75. package/dist-lib/lib/floriday-env.d.ts +29 -0
  76. package/dist-lib/lib/floriday-env.d.ts.map +1 -0
  77. package/dist-lib/lib/floriday-env.js +42 -0
  78. package/dist-lib/lib/floriday-env.server.d.ts +15 -0
  79. package/dist-lib/lib/floriday-env.server.d.ts.map +1 -0
  80. package/dist-lib/lib/floriday-env.server.js +13 -0
  81. package/dist-lib/lib/floriday-field-sources.d.ts +18 -0
  82. package/dist-lib/lib/floriday-field-sources.d.ts.map +1 -0
  83. package/dist-lib/lib/floriday-field-sources.js +45 -0
  84. package/dist-lib/lib/floriday-gateway-entities.d.ts +44 -0
  85. package/dist-lib/lib/floriday-gateway-entities.d.ts.map +1 -0
  86. package/dist-lib/lib/floriday-gateway-entities.js +51 -0
  87. package/dist-lib/lib/floriday-gateway.functions.d.ts +39999 -0
  88. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -0
  89. package/dist-lib/lib/floriday-gateway.functions.js +68 -0
  90. package/dist-lib/lib/floriday-gateway.server.d.ts +122 -0
  91. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -0
  92. package/dist-lib/lib/floriday-gateway.server.js +230 -0
  93. package/dist-lib/lib/floriday-import-validator.d.ts +26 -0
  94. package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -0
  95. package/dist-lib/lib/floriday-import-validator.js +41 -0
  96. package/dist-lib/lib/floriday-payload.d.ts +47 -3
  97. package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
  98. package/dist-lib/lib/floriday-payload.js +179 -7
  99. package/dist-lib/lib/floriday-schema-to-zod.d.ts +9 -0
  100. package/dist-lib/lib/floriday-schema-to-zod.d.ts.map +1 -0
  101. package/dist-lib/lib/floriday-schema-to-zod.js +53 -0
  102. package/dist-lib/lib/floriday-writes.functions.d.ts +19998 -0
  103. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -0
  104. package/dist-lib/lib/floriday-writes.functions.js +74 -0
  105. package/dist-lib/lib/image-resize.d.ts +43 -0
  106. package/dist-lib/lib/image-resize.d.ts.map +1 -0
  107. package/dist-lib/lib/image-resize.js +212 -0
  108. package/dist-lib/lib/normalize-errors-middleware.d.ts +2 -0
  109. package/dist-lib/lib/normalize-errors-middleware.d.ts.map +1 -0
  110. package/dist-lib/lib/normalize-errors-middleware.js +24 -0
  111. package/dist-lib/lib/wizard-step-fields.d.ts +15 -0
  112. package/dist-lib/lib/wizard-step-fields.d.ts.map +1 -0
  113. package/dist-lib/lib/wizard-step-fields.js +51 -0
  114. package/package.json +35 -14
@@ -4,6 +4,24 @@
4
4
  // Belangrijk: dit is een PURE module - geen Supabase / fetch calls.
5
5
  // De wizard bouwt het WizardData object en roept hier `buildAddTradeItemPayload`.
6
6
  import { z } from "zod";
7
+ /** Hardcoded lijst met aangesloten verpakkingsleveranciers zoals in Floriday. */
8
+ export const PACKAGING_SUPPLIERS = [
9
+ { id: "anylabel", name: "Anylabel" },
10
+ { id: "chrysal", name: "Chrysal" },
11
+ { id: "comdec", name: "Comdec" },
12
+ { id: "decowraps", name: "DecoWraps" },
13
+ { id: "desch-plantpak", name: "Desch Plantpak" },
14
+ { id: "floralife", name: "FloraLife" },
15
+ { id: "hb-rts-holding", name: "HB RTS Holding" },
16
+ { id: "jbb-packaging", name: "JBB Packaging" },
17
+ { id: "koen-pack", name: "Koen Pack" },
18
+ { id: "modiform", name: "Modiform" },
19
+ { id: "multi-supplies", name: "Multi Supplies" },
20
+ { id: "paardekooper", name: "Paardekooper Verpakkingen" },
21
+ { id: "poppelmann-teku", name: "Pöppelmann Teku" },
22
+ { id: "royal-floraholland", name: "Royal FloraHolland" },
23
+ { id: "twinpack", name: "Twinpack" },
24
+ ];
7
25
  // -------- GTIN check (Mod-10) ---------------------------------------------
8
26
  export function isValidGtin13(gtin) {
9
27
  if (!/^\d{13}$/.test(gtin))
@@ -57,12 +75,28 @@ export const stepSchemas = {
57
75
  }),
58
76
  beladingen: z.object({
59
77
  packingConfigurations: z
60
- .array(z.object({
61
- packagingType: z.string().min(1, "Fusttype verplicht"),
78
+ .array(z
79
+ .object({
80
+ source: z.enum(["VBN", "SUPPLIER"]).default("VBN"),
81
+ packagingType: z.string().default(""),
62
82
  loadCarrier: z.string().min(1, "Ladingdrager verplicht"),
83
+ packagingSupplierId: z.string().optional(),
63
84
  unitsPerPackage: z.number().int().min(1),
64
85
  packagesPerLayer: z.number().int().min(1),
65
86
  layersPerLoadCarrier: z.number().int().min(1),
87
+ isCustomerSpecific: z.boolean().default(false),
88
+ customerOrganizationIds: z.array(z.string()).default([]),
89
+ })
90
+ .refine((v) => (v.source === "VBN" ? v.packagingType.length > 0 : true), { message: "Fusttype (VBN) verplicht", path: ["packagingType"] })
91
+ .refine((v) => v.source === "SUPPLIER"
92
+ ? Boolean(v.packagingSupplierId) && v.packagingType.length > 0
93
+ : true, {
94
+ message: "Kies een verpakkingsleverancier én een fallback VBN-fust",
95
+ path: ["packagingSupplierId"],
96
+ })
97
+ .refine((v) => v.isCustomerSpecific ? v.customerOrganizationIds.length > 0 : true, {
98
+ message: "Kies minstens één klant voor klantspecifieke belading",
99
+ path: ["customerOrganizationIds"],
66
100
  }))
67
101
  .min(1, "Minstens één beladingsconfiguratie"),
68
102
  }),
@@ -135,9 +169,14 @@ const LOAD_CARRIER_VALUES = [
135
169
  "EURO_TROLLEY", "PALLET", "EURO_PALLET",
136
170
  ];
137
171
  function normaliseLoadCarrier(input) {
138
- const v = (input ?? "").trim().toUpperCase().replace(/[\s-]+/g, "_");
139
- return (LOAD_CARRIER_VALUES.includes(v)
140
- ? v : "NONE");
172
+ const raw = (input ?? "").trim();
173
+ if (/^\d+$/.test(raw))
174
+ return Number(raw);
175
+ const v = raw.toUpperCase().replace(/[\s-]+/g, "_");
176
+ if (LOAD_CARRIER_VALUES.includes(v)) {
177
+ return v;
178
+ }
179
+ return raw || "NONE";
141
180
  }
142
181
  function toIntOrUndefined(v) {
143
182
  if (v === undefined || v === null || v === "")
@@ -150,8 +189,8 @@ export function buildPackingConfigPayload(p, primary = false) {
150
189
  if (!vbnPackageCode) {
151
190
  throw new Error(`Fusttype (VBN package code) ontbreekt of is geen getal voor configuratie "${p.id}"`);
152
191
  }
153
- return {
154
- primary,
192
+ const payload = {
193
+ primary: primary || Boolean(p.isPrimary),
155
194
  piecesPerPackage: Math.max(1, Math.min(9999, Number(p.unitsPerPackage) || 1)),
156
195
  package: { vbnPackageCode },
157
196
  packagesPerLayer: Math.max(1, Math.min(9999, Number(p.packagesPerLayer) || 1)),
@@ -159,6 +198,16 @@ export function buildPackingConfigPayload(p, primary = false) {
159
198
  loadCarrier: normaliseLoadCarrier(p.loadCarrier),
160
199
  isHiddenForDirectSales: false,
161
200
  };
201
+ if (p.source === "SUPPLIER" && p.packagingSupplierId) {
202
+ payload.packagingSupplierId = p.packagingSupplierId;
203
+ if (p.packagingMaterialId) {
204
+ payload.packagingSupplierMaterialId = p.packagingMaterialId;
205
+ }
206
+ }
207
+ if (p.isCustomerSpecific && p.customerOrganizationIds.length > 0) {
208
+ payload.customerOrganizationIds = p.customerOrganizationIds;
209
+ }
210
+ return payload;
162
211
  }
163
212
  function weeksToSeasonalPeriods(weeks) {
164
213
  const sorted = [...new Set(weeks.filter((w) => w >= 1 && w <= 53))]
@@ -248,3 +297,126 @@ export const emptyWizardData = {
248
297
  yearRound: true,
249
298
  weeks: [],
250
299
  };
300
+ // -------- Reverse mapper: Floriday cache row -> WizardData ----------------
301
+ // Used by the detail/edit page to hydrate the wizard from an existing
302
+ // published trade item.
303
+ function productGroupFromVbn(vbnProductCode) {
304
+ const n = Number(vbnProductCode);
305
+ if (!Number.isFinite(n) || n <= 0)
306
+ return null;
307
+ const s = String(Math.floor(n));
308
+ if (s.startsWith("10"))
309
+ return "CUT_FLOWER";
310
+ if (s.startsWith("20"))
311
+ return "POT_PLANT";
312
+ if (s.startsWith("30"))
313
+ return "GARDEN_PLANT";
314
+ return null;
315
+ }
316
+ function pickName(value) {
317
+ if (!value)
318
+ return "";
319
+ if (typeof value === "string")
320
+ return value;
321
+ if (typeof value === "object") {
322
+ const o = value;
323
+ const v = o.nl ?? o.en ?? Object.values(o)[0];
324
+ return typeof v === "string" ? v : "";
325
+ }
326
+ return "";
327
+ }
328
+ function pickNameEn(value) {
329
+ if (value && typeof value === "object") {
330
+ const o = value;
331
+ return typeof o.en === "string" ? o.en : "";
332
+ }
333
+ return "";
334
+ }
335
+ function expandSeasonalPeriods(periods) {
336
+ if (!Array.isArray(periods) || periods.length === 0)
337
+ return [];
338
+ const out = new Set();
339
+ for (const p of periods) {
340
+ const s = Number(p?.startWeek);
341
+ const e = Number(p?.endWeek ?? p?.startWeek);
342
+ if (!Number.isFinite(s) || !Number.isFinite(e))
343
+ continue;
344
+ for (let w = Math.max(1, s); w <= Math.min(53, e); w++)
345
+ out.add(w);
346
+ }
347
+ return [...out].sort((a, b) => a - b);
348
+ }
349
+ export function floridayCacheToWizardData(raw) {
350
+ if (!raw || typeof raw !== "object")
351
+ return { ...emptyWizardData };
352
+ const vbn = raw.vbnProductCode ?? raw.vbnCode ?? "";
353
+ const characteristics = Array.isArray(raw.characteristics) ? raw.characteristics : [];
354
+ const features = {};
355
+ for (const c of characteristics) {
356
+ if (c?.vbnCode)
357
+ features[String(c.vbnCode)] = String(c.vbnValueCode ?? "");
358
+ }
359
+ const photos = Array.isArray(raw.photos) ? raw.photos : [];
360
+ const images = photos
361
+ .slice()
362
+ .sort((a, b) => (a?.sortIndex ?? 0) - (b?.sortIndex ?? 0))
363
+ .filter((p) => typeof p?.url === "string" && p.url.length > 0)
364
+ .map((p, i) => ({
365
+ url: p.url,
366
+ primary: Boolean(p.primary) || i === 0,
367
+ sortOrder: p.sortIndex ?? i,
368
+ }));
369
+ const packings = Array.isArray(raw.packingConfigurations)
370
+ ? raw.packingConfigurations
371
+ : [];
372
+ const packingConfigurations = packings.map((p, i) => ({
373
+ id: `existing-${i}`,
374
+ source: p?.packagingSupplierId ? "SUPPLIER" : "VBN",
375
+ packagingType: String(p?.package?.vbnPackageCode ?? ""),
376
+ loadCarrier: String(p?.loadCarrier ?? "NONE"),
377
+ packagingSupplierId: p?.packagingSupplierId ?? undefined,
378
+ packagingMaterialId: p?.packagingSupplierMaterialId ?? undefined,
379
+ unitsPerPackage: Number(p?.piecesPerPackage ?? 1),
380
+ packagesPerLayer: Number(p?.packagesPerLayer ?? 1),
381
+ layersPerLoadCarrier: Number(p?.layersPerLoadCarrier ?? 1),
382
+ isCustomerSpecific: Array.isArray(p?.customerOrganizationIds) && p.customerOrganizationIds.length > 0,
383
+ customerOrganizationIds: Array.isArray(p?.customerOrganizationIds) ? p.customerOrganizationIds : [],
384
+ isPrimary: Boolean(p?.primary) || i === 0,
385
+ }));
386
+ const customerOrgIds = Array.isArray(raw.customerOrganizationIds)
387
+ ? raw.customerOrganizationIds
388
+ : [];
389
+ const botanicalNames = Array.isArray(raw.botanicalNames)
390
+ ? raw.botanicalNames
391
+ : [];
392
+ const countries = Array.isArray(raw.countryOfOriginIsoCodes)
393
+ ? raw.countryOfOriginIsoCodes
394
+ : [];
395
+ const seasonalPeriods = Array.isArray(raw.seasonalPeriods)
396
+ ? raw.seasonalPeriods
397
+ : [];
398
+ const weeks = expandSeasonalPeriods(seasonalPeriods);
399
+ return {
400
+ productGroup: productGroupFromVbn(vbn),
401
+ vbnCode: vbn ? String(vbn) : "",
402
+ vbnName: "",
403
+ articleCode: String(raw.supplierArticleCode ?? raw.articleCode ?? ""),
404
+ name: pickName(raw.tradeItemName ?? raw.name),
405
+ nameEn: pickNameEn(raw.tradeItemName ?? raw.name),
406
+ gtin: String(raw.articleGtin ?? raw.gtin ?? ""),
407
+ collections: [],
408
+ articleType: customerOrgIds.length > 0 ? "CUSTOMER_SPECIFIC" : "REGULAR",
409
+ visibleInExplorer: !raw.isHiddenInCatalog,
410
+ botanicalName: botanicalNames.join("; "),
411
+ countryOfOrigin: countries.join(","),
412
+ producerCode: "",
413
+ lotNumber: "",
414
+ features,
415
+ customerOrganizationIds: customerOrgIds,
416
+ images,
417
+ packingConfigurations,
418
+ additionalPackaging: [],
419
+ yearRound: weeks.length === 0,
420
+ weeks,
421
+ };
422
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Bouwt een Zod-schema op basis van de fields[]-projectie van /gateway/schema.
3
+ * Wordt gebruikt voor snelle client-side UX-feedback. De gateway valideert
4
+ * altijd nogmaals server-side (validateEnums + Floriday zelf).
5
+ */
6
+ import { z, type ZodTypeAny } from "zod";
7
+ import type { FloridaySchemaField } from "../hooks/useFloriday";
8
+ export declare function buildZodFromFields(fields: FloridaySchemaField[]): z.ZodObject<Record<string, ZodTypeAny>>;
9
+ //# sourceMappingURL=floriday-schema-to-zod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floriday-schema-to-zod.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-schema-to-zod.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAmChE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAAE,GAC5B,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAIzC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Bouwt een Zod-schema op basis van de fields[]-projectie van /gateway/schema.
3
+ * Wordt gebruikt voor snelle client-side UX-feedback. De gateway valideert
4
+ * altijd nogmaals server-side (validateEnums + Floriday zelf).
5
+ */
6
+ import { z } from "zod";
7
+ function fieldToZod(f) {
8
+ let schema;
9
+ if (f.enum && f.enum.length > 0) {
10
+ schema = z.enum(f.enum);
11
+ }
12
+ else if (f.type === "number" || f.type === "integer") {
13
+ let n = z.number();
14
+ if (typeof f.minimum === "number")
15
+ n = n.min(f.minimum);
16
+ if (typeof f.maximum === "number")
17
+ n = n.max(f.maximum);
18
+ if (f.type === "integer")
19
+ n = n.int();
20
+ schema = n;
21
+ }
22
+ else if (f.type === "boolean") {
23
+ schema = z.boolean();
24
+ }
25
+ else if (f.type === "array") {
26
+ schema = z.array(z.any());
27
+ }
28
+ else {
29
+ let s = z.string();
30
+ if (typeof f.minLength === "number")
31
+ s = s.min(f.minLength);
32
+ if (typeof f.maxLength === "number")
33
+ s = s.max(f.maxLength);
34
+ if (f.pattern) {
35
+ try {
36
+ s = s.regex(new RegExp(f.pattern));
37
+ }
38
+ catch {
39
+ // ongeldige pattern uit schema → negeren
40
+ }
41
+ }
42
+ schema = s;
43
+ }
44
+ if (!f.required)
45
+ schema = schema.optional().nullable();
46
+ return schema;
47
+ }
48
+ export function buildZodFromFields(fields) {
49
+ const shape = {};
50
+ for (const f of fields)
51
+ shape[f.name] = fieldToZod(f);
52
+ return z.object(shape);
53
+ }