@teleporthq/teleport-plugin-next-data-source 0.43.59 → 0.43.60

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 (127) hide show
  1. package/__tests__/_helpers/pagination-widget-fixture.ts +230 -0
  2. package/__tests__/ecommerce-product-discounts.test.ts +196 -0
  3. package/__tests__/infinite-scroll.test.ts +143 -0
  4. package/__tests__/numbered-pagination.test.ts +113 -0
  5. package/__tests__/page-reset-on-filter-sort.test.ts +182 -0
  6. package/__tests__/page-url-sync.test.ts +208 -0
  7. package/__tests__/product-price-sort.ts +313 -0
  8. package/__tests__/search-url-sync.test.ts +7 -3
  9. package/dist/cjs/array-mapper-pagination.d.ts +9 -0
  10. package/dist/cjs/array-mapper-pagination.d.ts.map +1 -1
  11. package/dist/cjs/array-mapper-pagination.js.map +1 -1
  12. package/dist/cjs/count-effect.d.ts +42 -0
  13. package/dist/cjs/count-effect.d.ts.map +1 -0
  14. package/dist/cjs/count-effect.js +81 -0
  15. package/dist/cjs/count-effect.js.map +1 -0
  16. package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
  17. package/dist/cjs/fetchers/postgresql.js +2 -1
  18. package/dist/cjs/fetchers/postgresql.js.map +1 -1
  19. package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
  20. package/dist/cjs/fetchers/teleport.js +3 -2
  21. package/dist/cjs/fetchers/teleport.js.map +1 -1
  22. package/dist/cjs/infinite-scroll.d.ts +65 -0
  23. package/dist/cjs/infinite-scroll.d.ts.map +1 -0
  24. package/dist/cjs/infinite-scroll.js +190 -0
  25. package/dist/cjs/infinite-scroll.js.map +1 -0
  26. package/dist/cjs/numbered-pagination.d.ts +42 -0
  27. package/dist/cjs/numbered-pagination.d.ts.map +1 -0
  28. package/dist/cjs/numbered-pagination.js +167 -0
  29. package/dist/cjs/numbered-pagination.js.map +1 -0
  30. package/dist/cjs/page-reset.d.ts +42 -0
  31. package/dist/cjs/page-reset.d.ts.map +1 -0
  32. package/dist/cjs/page-reset.js +116 -0
  33. package/dist/cjs/page-reset.js.map +1 -0
  34. package/dist/cjs/page-url-sync.d.ts +42 -0
  35. package/dist/cjs/page-url-sync.d.ts.map +1 -0
  36. package/dist/cjs/page-url-sync.js +180 -0
  37. package/dist/cjs/page-url-sync.js.map +1 -0
  38. package/dist/cjs/pagination-controls.d.ts +55 -0
  39. package/dist/cjs/pagination-controls.d.ts.map +1 -0
  40. package/dist/cjs/pagination-controls.js +159 -0
  41. package/dist/cjs/pagination-controls.js.map +1 -0
  42. package/dist/cjs/pagination-plugin.d.ts.map +1 -1
  43. package/dist/cjs/pagination-plugin.js +506 -160
  44. package/dist/cjs/pagination-plugin.js.map +1 -1
  45. package/dist/cjs/product-price-sort.d.ts +116 -0
  46. package/dist/cjs/product-price-sort.d.ts.map +1 -0
  47. package/dist/cjs/product-price-sort.js +169 -0
  48. package/dist/cjs/product-price-sort.js.map +1 -0
  49. package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
  50. package/dist/cjs/transformations/ecommerce-product.js +1 -1
  51. package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
  52. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  53. package/dist/esm/array-mapper-pagination.d.ts +9 -0
  54. package/dist/esm/array-mapper-pagination.d.ts.map +1 -1
  55. package/dist/esm/array-mapper-pagination.js.map +1 -1
  56. package/dist/esm/count-effect.d.ts +42 -0
  57. package/dist/esm/count-effect.d.ts.map +1 -0
  58. package/dist/esm/count-effect.js +53 -0
  59. package/dist/esm/count-effect.js.map +1 -0
  60. package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
  61. package/dist/esm/fetchers/postgresql.js +2 -1
  62. package/dist/esm/fetchers/postgresql.js.map +1 -1
  63. package/dist/esm/fetchers/teleport.d.ts.map +1 -1
  64. package/dist/esm/fetchers/teleport.js +3 -2
  65. package/dist/esm/fetchers/teleport.js.map +1 -1
  66. package/dist/esm/infinite-scroll.d.ts +65 -0
  67. package/dist/esm/infinite-scroll.d.ts.map +1 -0
  68. package/dist/esm/infinite-scroll.js +159 -0
  69. package/dist/esm/infinite-scroll.js.map +1 -0
  70. package/dist/esm/numbered-pagination.d.ts +42 -0
  71. package/dist/esm/numbered-pagination.d.ts.map +1 -0
  72. package/dist/esm/numbered-pagination.js +138 -0
  73. package/dist/esm/numbered-pagination.js.map +1 -0
  74. package/dist/esm/page-reset.d.ts +42 -0
  75. package/dist/esm/page-reset.d.ts.map +1 -0
  76. package/dist/esm/page-reset.js +87 -0
  77. package/dist/esm/page-reset.js.map +1 -0
  78. package/dist/esm/page-url-sync.d.ts +42 -0
  79. package/dist/esm/page-url-sync.d.ts.map +1 -0
  80. package/dist/esm/page-url-sync.js +152 -0
  81. package/dist/esm/page-url-sync.js.map +1 -0
  82. package/dist/esm/pagination-controls.d.ts +55 -0
  83. package/dist/esm/pagination-controls.d.ts.map +1 -0
  84. package/dist/esm/pagination-controls.js +126 -0
  85. package/dist/esm/pagination-controls.js.map +1 -0
  86. package/dist/esm/pagination-plugin.d.ts.map +1 -1
  87. package/dist/esm/pagination-plugin.js +507 -161
  88. package/dist/esm/pagination-plugin.js.map +1 -1
  89. package/dist/esm/product-price-sort.d.ts +116 -0
  90. package/dist/esm/product-price-sort.d.ts.map +1 -0
  91. package/dist/esm/product-price-sort.js +160 -0
  92. package/dist/esm/product-price-sort.js.map +1 -0
  93. package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
  94. package/dist/esm/transformations/ecommerce-product.js +2 -2
  95. package/dist/esm/transformations/ecommerce-product.js.map +1 -1
  96. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  97. package/package.json +5 -5
  98. package/src/array-mapper-pagination.ts +10 -0
  99. package/src/count-effect.ts +137 -0
  100. package/src/fetchers/postgresql.ts +52 -13
  101. package/src/fetchers/teleport.ts +53 -13
  102. package/src/infinite-scroll.ts +401 -0
  103. package/src/numbered-pagination.ts +292 -0
  104. package/src/page-reset.ts +162 -0
  105. package/src/page-url-sync.ts +241 -0
  106. package/src/pagination-controls.ts +176 -0
  107. package/src/pagination-plugin.ts +755 -346
  108. package/src/product-price-sort.ts +204 -0
  109. package/src/transformations/ecommerce-product.ts +33 -7
  110. package/dist/cjs/array-mapper-registry.d.ts +0 -85
  111. package/dist/cjs/array-mapper-registry.d.ts.map +0 -1
  112. package/dist/cjs/array-mapper-registry.js +0 -296
  113. package/dist/cjs/array-mapper-registry.js.map +0 -1
  114. package/dist/cjs/pagination-with-count.d.ts +0 -6
  115. package/dist/cjs/pagination-with-count.d.ts.map +0 -1
  116. package/dist/cjs/pagination-with-count.js +0 -63
  117. package/dist/cjs/pagination-with-count.js.map +0 -1
  118. package/dist/esm/array-mapper-registry.d.ts +0 -85
  119. package/dist/esm/array-mapper-registry.d.ts.map +0 -1
  120. package/dist/esm/array-mapper-registry.js +0 -292
  121. package/dist/esm/array-mapper-registry.js.map +0 -1
  122. package/dist/esm/pagination-with-count.d.ts +0 -6
  123. package/dist/esm/pagination-with-count.d.ts.map +0 -1
  124. package/dist/esm/pagination-with-count.js +0 -34
  125. package/dist/esm/pagination-with-count.js.map +0 -1
  126. package/src/array-mapper-registry.ts +0 -416
  127. package/src/pagination-with-count.ts +0 -89
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Ordering a products list by the price customers actually pay.
3
+ *
4
+ * A per-product discount is a SCHEDULED VIEW of the price: `teleport_products.
5
+ * price` holds the list price and never moves, while `teleport_products.
6
+ * discounts` holds a JSON array of markdowns with `[startsAt, endsAt)` windows.
7
+ * Every display surface resolves that at read time.
8
+ *
9
+ * Sorting is the one surface that cannot, because it happens in SQL before the
10
+ * rows ever reach JavaScript — and the list is paginated, so re-sorting the
11
+ * fetched page would only reorder 20 of N products. The ordering therefore has
12
+ * to be computed by the database, which is what this expression does: an inline
13
+ * scalar sub-select over the JSON column, evaluated per query against `now()`.
14
+ *
15
+ * Deliberately NOT a stored column. A materialised "effective price" is correct
16
+ * only until a schedule boundary passes with nobody writing that row, and
17
+ * keeping it fresh needs a job no generated store has.
18
+ *
19
+ * ## The expression cannot raise
20
+ *
21
+ * It runs inside `ORDER BY` for the whole products table, so one unparseable
22
+ * row would fail the entire query and empty the page. Every cast is therefore
23
+ * guarded:
24
+ *
25
+ * - the JSON cast only happens for text that already looks like an array;
26
+ * - `value` is cast only when it matches a bare decimal;
27
+ * - the window bounds are compared as TEXT, never cast to a timestamp. The
28
+ * editor writes them with `toISOString()`, whose fixed-width UTC format
29
+ * (`YYYY-MM-DDTHH:MM:SS.sssZ`) sorts lexicographically in the same order it
30
+ * sorts chronologically.
31
+ *
32
+ * The caller adds a second net: a query using this expression falls back to a
33
+ * plain column sort if the database rejects it, so an unforeseen shape degrades
34
+ * to the previous ordering rather than to a broken page.
35
+ */
36
+
37
+ /** The e-commerce products table — the only table this ordering applies to. */
38
+ export const PRODUCTS_TABLE_NAME = 'teleport_products'
39
+
40
+ /** The sort field the storefront's "Price (low to high / high to low)" emits. */
41
+ export const PRODUCT_PRICE_SORT_FIELD = 'price'
42
+
43
+ /**
44
+ * The sort field the storefront's "Discount (low to high / high to low)" emits.
45
+ *
46
+ * Deliberately the name of a REAL column rather than a virtual one like
47
+ * `discount`. The same field travels to sixteen different fetchers and to the
48
+ * editor's own data API, none of which know about this rewrite — and a name
49
+ * that is not a column becomes `ORDER BY discount` and fails the whole query.
50
+ * Using the column the markdowns are stored in means the worst any unaware
51
+ * backend can do is order by the raw JSON: arbitrary, but never an error.
52
+ */
53
+ export const PRODUCT_DISCOUNT_SORT_FIELD = 'discounts'
54
+
55
+ /**
56
+ * The column a rewritten sort falls back to when the expression cannot run —
57
+ * guaranteed to exist on the products table, which `discounts` itself is not on
58
+ * a store provisioned before discounts shipped.
59
+ */
60
+ export const PRODUCT_SORT_FALLBACK_COLUMN = 'price'
61
+
62
+ /**
63
+ * `now()` as the same fixed-width UTC string the editor stores its bounds in,
64
+ * so the window comparison is pure text and can never raise on a bad date.
65
+ */
66
+ const NOW_AS_ISO_TEXT = `to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`
67
+
68
+ /**
69
+ * Only text that already looks like a JSON array is cast; anything else — NULL,
70
+ * empty, or a hand-edited scalar — reads as "no discounts".
71
+ */
72
+ const SAFE_DISCOUNTS_JSONB = `CASE
73
+ WHEN "discounts" IS NULL OR btrim("discounts") NOT LIKE '[%' THEN '[]'::jsonb
74
+ ELSE "discounts"::jsonb
75
+ END`
76
+
77
+ /**
78
+ * The price a product sells for right now, for use in an `ORDER BY` over
79
+ * `teleport_products`. Falls back to the list price when no markdown is live.
80
+ *
81
+ * Mirrors `resolveActiveDiscount` + `computeDiscountedPrice`: the FIRST live
82
+ * entry in array order wins (hence `WITH ORDINALITY` and `ORDER BY ord`),
83
+ * unknown types and non-positive values are ignored, a percentage is capped at
84
+ * 100, and the result is floored at zero and rounded to the cent.
85
+ */
86
+ export const buildProductEffectivePriceSql = (): string =>
87
+ `COALESCE((
88
+ SELECT ROUND(GREATEST(0,
89
+ CASE
90
+ WHEN entry.kind = 'percentage'
91
+ THEN "price"::numeric * (1 - LEAST(entry.amount, 100) / 100)
92
+ ELSE "price"::numeric - entry.amount
93
+ END
94
+ ), 2)
95
+ FROM (
96
+ SELECT
97
+ d.value->>'type' AS kind,
98
+ CASE
99
+ WHEN d.value->>'value' ~ '^[0-9]+(\\.[0-9]+)?$' THEN (d.value->>'value')::numeric
100
+ ELSE 0
101
+ END AS amount,
102
+ d.value->>'startsAt' AS starts_at,
103
+ d.value->>'endsAt' AS ends_at,
104
+ d.ord AS ord
105
+ FROM jsonb_array_elements(${SAFE_DISCOUNTS_JSONB}) WITH ORDINALITY AS d(value, ord)
106
+ ) AS entry
107
+ WHERE entry.amount > 0
108
+ AND entry.kind IN ('percentage', 'fixed')
109
+ AND (entry.starts_at IS NULL OR entry.starts_at <= ${NOW_AS_ISO_TEXT})
110
+ AND (entry.ends_at IS NULL OR entry.ends_at > ${NOW_AS_ISO_TEXT})
111
+ ORDER BY entry.ord
112
+ LIMIT 1
113
+ ), "price"::numeric)`
114
+
115
+ /**
116
+ * How much a product is marked down RIGHT NOW, as a percentage of its list
117
+ * price — the metric "sort by discount" means.
118
+ *
119
+ * A percentage rather than an absolute saving, because it is the only figure
120
+ * comparable across a mixed catalogue: $20 off a $2000 sofa is a worse deal
121
+ * than $5 off a $10 mug, and sorting by the absolute amount would rank them the
122
+ * other way round.
123
+ *
124
+ * Zero when nothing is live, so undiscounted products collect at one end
125
+ * instead of scattering. `price > 0` is the divide-by-zero guard AND the NULL
126
+ * guard — `NULL > 0` is NULL, which is not true, so a priceless row takes the
127
+ * ELSE branch.
128
+ */
129
+ export const buildProductDiscountPercentageSql = (): string =>
130
+ `CASE
131
+ WHEN "price"::numeric > 0
132
+ THEN ROUND(((("price"::numeric - ${buildProductEffectivePriceSql()}) / "price"::numeric) * 100), 4)
133
+ ELSE 0
134
+ END`
135
+
136
+ /** Whether a generated fetcher for `tableName` should order prices by the markdown. */
137
+ export const supportsProductPriceSort = (tableName: string): boolean =>
138
+ tableName === PRODUCTS_TABLE_NAME
139
+
140
+ /**
141
+ * The complete `sortFieldSql` helper a SQL fetcher embeds in its generated
142
+ * route.
143
+ *
144
+ * Returned as one ready-made string, and the SQL is emitted through
145
+ * `JSON.stringify` — a double-quoted JS literal carries no backticks and no
146
+ * `${`, so it can be interpolated into a fetcher's template literal without any
147
+ * nested-escaping hazard. (Emitting it inline is how this first went wrong: a
148
+ * backtick inside an explanatory comment closed the enclosing template.)
149
+ *
150
+ * On a table that is not the products table the helper still exists but is the
151
+ * identity, so a fetcher can embed it unconditionally.
152
+ */
153
+ export const generateSortFieldSqlHelper = (tableName: string): string => {
154
+ const branches = supportsProductPriceSort(tableName)
155
+ ? [
156
+ ` if (field === ${JSON.stringify(PRODUCT_PRICE_SORT_FIELD)}) {`,
157
+ ` return ${JSON.stringify(buildProductEffectivePriceSql())}`,
158
+ ` }`,
159
+ ` if (field === ${JSON.stringify(PRODUCT_DISCOUNT_SORT_FIELD)}) {`,
160
+ ` return ${JSON.stringify(buildProductDiscountPercentageSql())}`,
161
+ ` }`,
162
+ ].join('\n') + '\n'
163
+ : ''
164
+
165
+ return [
166
+ '// Maps a requested sort field to the SQL that orders by it. Only the',
167
+ "// products table's price and discounts are rewritten - into the price",
168
+ '// customers actually pay today, and into how far each product is marked',
169
+ '// down. Every other field is passed through, so this can never change an',
170
+ '// unrelated sort.',
171
+ 'function sortFieldSql(field) {',
172
+ branches + ' return field',
173
+ '}',
174
+ ].join('\n')
175
+ }
176
+
177
+ /**
178
+ * The plain-column ORDER BY used when the rewritten expression cannot run.
179
+ *
180
+ * Both rewritten fields fall back to `price`, never to themselves: `discounts`
181
+ * is the JSON column, and ordering a page by raw JSON is meaningless — while a
182
+ * store provisioned before discounts shipped has no such column at all, so
183
+ * retrying with it would fail exactly like the expression did.
184
+ */
185
+ export const generateSortFallbackFieldHelper = (tableName: string): string => {
186
+ const branches = supportsProductPriceSort(tableName)
187
+ ? ` if (field === ${JSON.stringify(PRODUCT_PRICE_SORT_FIELD)} || field === ${JSON.stringify(
188
+ PRODUCT_DISCOUNT_SORT_FIELD
189
+ )}) {\n return ${JSON.stringify(PRODUCT_SORT_FALLBACK_COLUMN)}\n }\n`
190
+ : ''
191
+
192
+ return ['function sortFallbackField(field) {', branches + ' return field', '}'].join('\n')
193
+ }
194
+
195
+ /**
196
+ * A deterministic tiebreaker appended to every products ORDER BY.
197
+ *
198
+ * Without one, a paginated sort with ties — and "sort by discount" ties every
199
+ * undiscounted product at zero — has no defined order between equal rows, so
200
+ * the same product can appear on two pages or on none. `id` is on the products
201
+ * table from its first migration.
202
+ */
203
+ export const generateSortTiebreakSql = (tableName: string): string =>
204
+ supportsProductPriceSort(tableName) ? ', "id" ASC' : ''
@@ -1,5 +1,5 @@
1
1
  import type { UIDLEcommerceCategory } from '@teleporthq/teleport-types'
2
- import { StorefrontTax } from '@teleporthq/teleport-shared'
2
+ import { ProductDiscounts, StorefrontTax } from '@teleporthq/teleport-shared'
3
3
 
4
4
  /**
5
5
  * Everything the product transform needs baked in at export time, beyond the
@@ -85,6 +85,7 @@ export const generateEcommerceProductTransformationCode = (
85
85
 
86
86
  return `
87
87
  ${taxHelperCode}
88
+ ${ProductDiscounts.generateProductDiscountHelperCode()}
88
89
 
89
90
  // Baked from the merchant's stock settings: TRUE when stock never blocks a
90
91
  // purchase (stock management off OR backorders allowed). Regenerating the
@@ -472,24 +473,45 @@ function buildEcommerceProduct(record, options) {
472
473
  // gross, because nothing writes them back.
473
474
  //
474
475
  // MUST mirror packages/renderer/src/utils/ecommerce-products.ts.
475
- var displayPrice = grossMoney(price)
476
+ // The scheduled markdown in force right now, resolved from the raw column on
477
+ // every read so a discount starts and expires on its own. It comes off the NET
478
+ // price BEFORE tax: tax is a percentage of what is actually charged.
479
+ var activeDiscount = __pdResolveActive(record.discounts, Date.now())
480
+ var displayPrice = grossMoney(__pdDiscountedPrice(price, activeDiscount))
481
+ var defaultVariantNetPrice = firstVariant
482
+ ? safeNumber(formatVariantPrice(firstVariant.price, price), 0)
483
+ : 0
476
484
  var defaultVariantDisplayPrice = firstVariant
477
- ? grossMoney(formatVariantPrice(firstVariant.price, price))
485
+ ? grossMoney(__pdDiscountedPrice(defaultVariantNetPrice, activeDiscount))
478
486
  : ''
479
487
  var variantsDisplay = []
480
488
  for (var dv = 0; dv < variants.length; dv++) {
481
489
  var dvRow = variants[dv]
490
+ // A null variant price INHERITS the product price, so discount and gross the
491
+ // RESOLVED value — otherwise an inheriting combination would fall back to
492
+ // the net base price in the picker while an overriding one showed gross.
493
+ var dvNetPrice = dvRow.price == null ? price : dvRow.price
482
494
  variantsDisplay.push({
483
495
  id: dvRow.id,
484
496
  options: dvRow.options,
485
- // A null variant price INHERITS the product price, so gross the RESOLVED
486
- // value — otherwise an inheriting combination would fall back to the net
487
- // base price in the picker while an overriding one showed gross.
488
- price: applyStorefrontTax(dvRow.price == null ? price : dvRow.price),
497
+ price: applyStorefrontTax(__pdDiscountedPrice(dvNetPrice, activeDiscount)),
498
+ // What this combination costs WITHOUT the discount, for the struck-through
499
+ // price beside it. Null when nothing is discounted, so the picker's click
500
+ // resolver can tell "no saving" from "a saving of zero".
501
+ originalPrice: activeDiscount ? applyStorefrontTax(dvNetPrice) : null,
489
502
  quantity: dvRow.quantity,
490
503
  image_url: dvRow.image_url,
491
504
  })
492
505
  }
506
+ // Discount display fields. Strings throughout: rendering conditions compare
507
+ // operands as strings, and a bound text node prints its value verbatim.
508
+ var hasDiscount = activeDiscount ? 'true' : 'false'
509
+ var discountLabel = activeDiscount
510
+ ? __pdDiscountLabel(activeDiscount, currencySymbol, getCurrencyInfo(currency).position)
511
+ : ''
512
+ var originalDisplayPrice = activeDiscount ? grossMoney(price) : ''
513
+ var originalDefaultVariantDisplayPrice =
514
+ activeDiscount && firstVariant ? grossMoney(defaultVariantNetPrice) : ''
493
515
  // Can this product be added to the cart at all, before the shopper touches the
494
516
  // picker? A STRING, same convention as outOfStock/requiresVariantSelection.
495
517
  //
@@ -571,6 +593,10 @@ function buildEcommerceProduct(record, options) {
571
593
  defaultVariantPrice: defaultVariantPrice,
572
594
  displayPrice: displayPrice,
573
595
  defaultVariantDisplayPrice: defaultVariantDisplayPrice,
596
+ hasDiscount: hasDiscount,
597
+ discountLabel: discountLabel,
598
+ originalDisplayPrice: originalDisplayPrice,
599
+ originalDefaultVariantDisplayPrice: originalDefaultVariantDisplayPrice,
574
600
  hasPurchasableVariant: hasPurchasableVariant,
575
601
  }
576
602
  }
@@ -1,85 +0,0 @@
1
- /**
2
- * ArrayMapperRegistry - Centralized registry for managing array mappers,
3
- * their associated states, search inputs, and pagination buttons.
4
- *
5
- * This ensures consistent index usage across:
6
- * - Component state declarations
7
- * - DataProvider params
8
- * - getStaticProps fetch calls and prop names
9
- * - Search input bindings
10
- * - Pagination button bindings
11
- */
12
- export type MapperType = 'paginated' | 'pagination-only' | 'search-only' | 'plain';
13
- export interface ArrayMapperRegistryEntry {
14
- registryIndex: number;
15
- type: MapperType;
16
- dataSourceIdentifier: string;
17
- arrayMapperRenderProp: string;
18
- uidlGlobalIndex: number;
19
- paginated: boolean;
20
- searchEnabled: boolean;
21
- perPage: number;
22
- searchDebounce: number;
23
- searchDefaultValue?: string;
24
- queryColumns: string[];
25
- stateVarPrefix: string;
26
- pageStateVar?: string;
27
- setPageStateVar?: string;
28
- maxPagesStateVar?: string;
29
- setMaxPagesStateVar?: string;
30
- combinedStateVar?: string;
31
- setCombinedStateVar?: string;
32
- searchQueryVar?: string;
33
- setSearchQueryVar?: string;
34
- debouncedSearchQueryVar?: string;
35
- setDebouncedSearchQueryVar?: string;
36
- skipDebounceRefVar?: string;
37
- getStaticPropsDataVar: string;
38
- getStaticPropsMaxPagesVar: string;
39
- getStaticPropsPropName: string;
40
- getStaticPropsMaxPagesPropName: string;
41
- searchInputClass?: string;
42
- paginationNodeClass?: string;
43
- prevButtonClass?: string;
44
- nextButtonClass?: string;
45
- dataProviderJSX?: any;
46
- searchInputJSX?: any;
47
- }
48
- export declare class ArrayMapperRegistry {
49
- private entries;
50
- private paginatedCount;
51
- private paginationOnlyCount;
52
- private searchOnlyCount;
53
- private plainCount;
54
- registerFromUIDL(config: {
55
- dataSourceIdentifier: string;
56
- arrayMapperRenderProp: string;
57
- uidlGlobalIndex: number;
58
- paginated: boolean;
59
- searchEnabled: boolean;
60
- perPage?: number;
61
- searchDebounce?: number;
62
- searchDefaultValue?: string;
63
- queryColumns?: string[];
64
- }): ArrayMapperRegistryEntry;
65
- getEntry(registryIndex: number): ArrayMapperRegistryEntry | undefined;
66
- getEntryByUIDLIndex(uidlGlobalIndex: number): ArrayMapperRegistryEntry | undefined;
67
- getEntriesByType(type: MapperType): ArrayMapperRegistryEntry[];
68
- getAllEntries(): ArrayMapperRegistryEntry[];
69
- getPaginatedEntries(): ArrayMapperRegistryEntry[];
70
- getPaginationOnlyEntries(): ArrayMapperRegistryEntry[];
71
- getSearchOnlyEntries(): ArrayMapperRegistryEntry[];
72
- getPlainEntries(): ArrayMapperRegistryEntry[];
73
- updateJSXReferences(registryIndex: number, updates: Partial<Pick<ArrayMapperRegistryEntry, 'dataProviderJSX' | 'searchInputJSX' | 'searchInputClass' | 'paginationNodeClass' | 'prevButtonClass' | 'nextButtonClass'>>): void;
74
- getStateInitializationMaxPagesPropName(entry: ArrayMapperRegistryEntry): string;
75
- toDebugSummary(): string;
76
- private determineType;
77
- private getTypeIndex;
78
- private incrementTypeCount;
79
- private generateStateVarPrefix;
80
- private generateStateVarNames;
81
- private generateGetStaticPropsVarNames;
82
- private capitalize;
83
- }
84
- export declare function createRegistryFromUIDL(uidlNode: any, allResources: any): ArrayMapperRegistry;
85
- //# sourceMappingURL=array-mapper-registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"array-mapper-registry.d.ts","sourceRoot":"","sources":["../../src/array-mapper-registry.ts"],"names":[],"mappings":"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"}
@@ -1,296 +0,0 @@
1
- "use strict";
2
- /**
3
- * ArrayMapperRegistry - Centralized registry for managing array mappers,
4
- * their associated states, search inputs, and pagination buttons.
5
- *
6
- * This ensures consistent index usage across:
7
- * - Component state declarations
8
- * - DataProvider params
9
- * - getStaticProps fetch calls and prop names
10
- * - Search input bindings
11
- * - Pagination button bindings
12
- */
13
- var __assign = (this && this.__assign) || function () {
14
- __assign = Object.assign || function(t) {
15
- for (var s, i = 1, n = arguments.length; i < n; i++) {
16
- s = arguments[i];
17
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
18
- t[p] = s[p];
19
- }
20
- return t;
21
- };
22
- return __assign.apply(this, arguments);
23
- };
24
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
25
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
26
- if (ar || !(i in from)) {
27
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
28
- ar[i] = from[i];
29
- }
30
- }
31
- return to.concat(ar || Array.prototype.slice.call(from));
32
- };
33
- Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.createRegistryFromUIDL = exports.ArrayMapperRegistry = void 0;
35
- var ArrayMapperRegistry = /** @class */ (function () {
36
- function ArrayMapperRegistry() {
37
- this.entries = [];
38
- this.paginatedCount = 0;
39
- this.paginationOnlyCount = 0;
40
- this.searchOnlyCount = 0;
41
- this.plainCount = 0;
42
- }
43
- ArrayMapperRegistry.prototype.registerFromUIDL = function (config) {
44
- var type = this.determineType(config.paginated, config.searchEnabled);
45
- var typeIndex = this.getTypeIndex(type);
46
- var registryIndex = this.entries.length;
47
- var stateVarPrefix = this.generateStateVarPrefix(type, typeIndex);
48
- var entry = __assign(__assign({ registryIndex: registryIndex, type: type, dataSourceIdentifier: config.dataSourceIdentifier, arrayMapperRenderProp: config.arrayMapperRenderProp, uidlGlobalIndex: config.uidlGlobalIndex, paginated: config.paginated, searchEnabled: config.searchEnabled, perPage: config.perPage || 10, searchDebounce: config.searchDebounce || 300, searchDefaultValue: config.searchDefaultValue, queryColumns: config.queryColumns || [], stateVarPrefix: stateVarPrefix }, this.generateStateVarNames(stateVarPrefix, config.paginated, config.searchEnabled)), this.generateGetStaticPropsVarNames(config.dataSourceIdentifier, type, typeIndex));
49
- this.entries.push(entry);
50
- this.incrementTypeCount(type);
51
- return entry;
52
- };
53
- ArrayMapperRegistry.prototype.getEntry = function (registryIndex) {
54
- return this.entries[registryIndex];
55
- };
56
- ArrayMapperRegistry.prototype.getEntryByUIDLIndex = function (uidlGlobalIndex) {
57
- return this.entries.find(function (e) { return e.uidlGlobalIndex === uidlGlobalIndex; });
58
- };
59
- ArrayMapperRegistry.prototype.getEntriesByType = function (type) {
60
- return this.entries.filter(function (e) { return e.type === type; });
61
- };
62
- ArrayMapperRegistry.prototype.getAllEntries = function () {
63
- return __spreadArray([], this.entries, true);
64
- };
65
- ArrayMapperRegistry.prototype.getPaginatedEntries = function () {
66
- return this.getEntriesByType('paginated');
67
- };
68
- ArrayMapperRegistry.prototype.getPaginationOnlyEntries = function () {
69
- return this.getEntriesByType('pagination-only');
70
- };
71
- ArrayMapperRegistry.prototype.getSearchOnlyEntries = function () {
72
- return this.getEntriesByType('search-only');
73
- };
74
- ArrayMapperRegistry.prototype.getPlainEntries = function () {
75
- return this.getEntriesByType('plain');
76
- };
77
- ArrayMapperRegistry.prototype.updateJSXReferences = function (registryIndex, updates) {
78
- var entry = this.entries[registryIndex];
79
- if (entry) {
80
- Object.assign(entry, updates);
81
- }
82
- };
83
- ArrayMapperRegistry.prototype.getStateInitializationMaxPagesPropName = function (entry) {
84
- return entry.getStaticPropsMaxPagesPropName;
85
- };
86
- ArrayMapperRegistry.prototype.toDebugSummary = function () {
87
- return JSON.stringify(this.entries.map(function (e) { return ({
88
- registryIndex: e.registryIndex,
89
- type: e.type,
90
- dataSource: e.dataSourceIdentifier,
91
- renderProp: e.arrayMapperRenderProp,
92
- uidlIndex: e.uidlGlobalIndex,
93
- statePrefix: e.stateVarPrefix,
94
- getStaticPropsProp: e.getStaticPropsPropName,
95
- }); }), null, 2);
96
- };
97
- ArrayMapperRegistry.prototype.determineType = function (paginated, searchEnabled) {
98
- if (paginated && searchEnabled) {
99
- return 'paginated';
100
- }
101
- if (paginated && !searchEnabled) {
102
- return 'pagination-only';
103
- }
104
- if (!paginated && searchEnabled) {
105
- return 'search-only';
106
- }
107
- return 'plain';
108
- };
109
- ArrayMapperRegistry.prototype.getTypeIndex = function (type) {
110
- switch (type) {
111
- case 'paginated':
112
- return this.paginatedCount;
113
- case 'pagination-only':
114
- return this.paginationOnlyCount;
115
- case 'search-only':
116
- return this.searchOnlyCount;
117
- case 'plain':
118
- return this.plainCount;
119
- default:
120
- return 0;
121
- }
122
- };
123
- ArrayMapperRegistry.prototype.incrementTypeCount = function (type) {
124
- switch (type) {
125
- case 'paginated':
126
- this.paginatedCount++;
127
- break;
128
- case 'pagination-only':
129
- this.paginationOnlyCount++;
130
- break;
131
- case 'search-only':
132
- this.searchOnlyCount++;
133
- break;
134
- case 'plain':
135
- this.plainCount++;
136
- break;
137
- default:
138
- break;
139
- }
140
- };
141
- ArrayMapperRegistry.prototype.generateStateVarPrefix = function (type, typeIndex) {
142
- switch (type) {
143
- case 'paginated':
144
- return "pg_".concat(typeIndex);
145
- case 'pagination-only':
146
- return "paginationOnly_".concat(typeIndex);
147
- case 'search-only':
148
- return "searchOnly_".concat(typeIndex);
149
- case 'plain':
150
- return "plain_".concat(typeIndex);
151
- default:
152
- return '';
153
- }
154
- };
155
- ArrayMapperRegistry.prototype.generateStateVarNames = function (prefix, paginated, searchEnabled) {
156
- var result = {};
157
- if (paginated) {
158
- if (searchEnabled) {
159
- result.combinedStateVar = "paginationState_".concat(prefix);
160
- result.setCombinedStateVar = "setPaginationState_".concat(prefix);
161
- result.pageStateVar = "paginationState_".concat(prefix, ".page");
162
- result.setPageStateVar = "setPaginationState_".concat(prefix);
163
- }
164
- else {
165
- result.pageStateVar = "".concat(prefix, "_page");
166
- result.setPageStateVar = "set".concat(this.capitalize(prefix), "_page");
167
- }
168
- result.maxPagesStateVar = "pagination_".concat(prefix, "_maxPages");
169
- result.setMaxPagesStateVar = "setPagination_".concat(prefix, "_maxPages");
170
- }
171
- else {
172
- result.pageStateVar = '';
173
- result.setPageStateVar = '';
174
- result.maxPagesStateVar = '';
175
- result.setMaxPagesStateVar = '';
176
- }
177
- if (searchEnabled) {
178
- result.searchQueryVar = "search_".concat(prefix, "_query");
179
- result.setSearchQueryVar = "setSearch_".concat(prefix, "_query");
180
- result.debouncedSearchQueryVar = "debouncedSearch_".concat(prefix, "_query");
181
- result.setDebouncedSearchQueryVar = "setDebouncedSearch_".concat(prefix, "_query");
182
- result.skipDebounceRefVar = "skipDebounceOnMount_".concat(prefix);
183
- }
184
- return result;
185
- };
186
- ArrayMapperRegistry.prototype.generateGetStaticPropsVarNames = function (dataSourceIdentifier, type, typeIndex) {
187
- var suffix;
188
- switch (type) {
189
- case 'paginated':
190
- suffix = "pg_".concat(typeIndex);
191
- break;
192
- case 'pagination-only':
193
- suffix = "paginationOnly_".concat(typeIndex);
194
- break;
195
- case 'search-only':
196
- suffix = "searchOnly_".concat(typeIndex);
197
- break;
198
- case 'plain':
199
- suffix = "plain_".concat(typeIndex);
200
- break;
201
- default:
202
- suffix = "plain_".concat(typeIndex);
203
- break;
204
- }
205
- var dataVar = "".concat(dataSourceIdentifier, "_").concat(suffix);
206
- var maxPagesVar = "".concat(dataSourceIdentifier, "_").concat(suffix, "_maxPages");
207
- return {
208
- getStaticPropsDataVar: dataVar,
209
- getStaticPropsMaxPagesVar: maxPagesVar,
210
- getStaticPropsPropName: dataVar,
211
- getStaticPropsMaxPagesPropName: maxPagesVar,
212
- };
213
- };
214
- ArrayMapperRegistry.prototype.capitalize = function (str) {
215
- return str.charAt(0).toUpperCase() + str.slice(1);
216
- };
217
- return ArrayMapperRegistry;
218
- }());
219
- exports.ArrayMapperRegistry = ArrayMapperRegistry;
220
- function createRegistryFromUIDL(uidlNode, allResources) {
221
- var registry = new ArrayMapperRegistry();
222
- var visitedRepeaterKeys = new Set();
223
- var uidlGlobalIndex = 0;
224
- var findQueryColumns = function (resourceId, nodeResource, registryResources) {
225
- var _a, _b, _c, _d;
226
- if ((_a = nodeResource === null || nodeResource === void 0 ? void 0 : nodeResource.params) === null || _a === void 0 ? void 0 : _a.queryColumns) {
227
- var val = nodeResource.params.queryColumns;
228
- if (val.type === 'static' && Array.isArray(val.content)) {
229
- return val.content;
230
- }
231
- }
232
- if ((_d = (_c = (_b = registryResources === null || registryResources === void 0 ? void 0 : registryResources.items) === null || _b === void 0 ? void 0 : _b[resourceId]) === null || _c === void 0 ? void 0 : _c.params) === null || _d === void 0 ? void 0 : _d.queryColumns) {
233
- var val = registryResources.items[resourceId].params.queryColumns;
234
- if (val.type === 'static' && Array.isArray(val.content)) {
235
- return val.content;
236
- }
237
- }
238
- return undefined;
239
- };
240
- var traverse = function (node, currentDataSourceId) {
241
- var _a, _b, _c, _d, _e, _f;
242
- if (!node || typeof node !== 'object') {
243
- return;
244
- }
245
- if (node.type === 'data-source-list' && ((_a = node.content) === null || _a === void 0 ? void 0 : _a.renderPropIdentifier)) {
246
- currentDataSourceId = node.content.renderPropIdentifier;
247
- }
248
- if (node.type === 'cms-list-repeater' && ((_b = node.content) === null || _b === void 0 ? void 0 : _b.renderPropIdentifier)) {
249
- var c = node.content;
250
- var repeaterKey = "".concat(c.renderPropIdentifier, "|").concat(c.paginated, "|").concat(c.searchEnabled, "|").concat(c.perPage || 'default', "|").concat(c.source || '');
251
- if (visitedRepeaterKeys.has(repeaterKey)) {
252
- return;
253
- }
254
- visitedRepeaterKeys.add(repeaterKey);
255
- var resourceId = (_c = c.resource) === null || _c === void 0 ? void 0 : _c.id;
256
- var queryColumns = void 0;
257
- if (resourceId) {
258
- queryColumns = findQueryColumns(resourceId, c.resource, allResources);
259
- }
260
- var rawDefault = c.searchDefaultValue;
261
- var searchDefaultValue = rawDefault &&
262
- rawDefault.type === 'static' &&
263
- typeof rawDefault.content === 'string' &&
264
- rawDefault.content.length > 0
265
- ? rawDefault.content
266
- : undefined;
267
- registry.registerFromUIDL({
268
- dataSourceIdentifier: currentDataSourceId || 'unknown',
269
- arrayMapperRenderProp: c.renderPropIdentifier,
270
- uidlGlobalIndex: uidlGlobalIndex++,
271
- paginated: !!c.paginated,
272
- searchEnabled: !!c.searchEnabled,
273
- perPage: c.perPage,
274
- searchDebounce: c.searchDebounce,
275
- searchDefaultValue: searchDefaultValue,
276
- queryColumns: queryColumns,
277
- });
278
- }
279
- if (((_d = node.content) === null || _d === void 0 ? void 0 : _d.children) && Array.isArray(node.content.children)) {
280
- node.content.children.forEach(function (child) { return traverse(child, currentDataSourceId); });
281
- }
282
- if ((_e = node.content) === null || _e === void 0 ? void 0 : _e.node) {
283
- traverse(node.content.node, currentDataSourceId);
284
- }
285
- if ((_f = node.content) === null || _f === void 0 ? void 0 : _f.nodes) {
286
- Object.values(node.content.nodes).forEach(function (n) { return traverse(n, currentDataSourceId); });
287
- }
288
- if (Array.isArray(node.children)) {
289
- node.children.forEach(function (child) { return traverse(child, currentDataSourceId); });
290
- }
291
- };
292
- traverse(uidlNode);
293
- return registry;
294
- }
295
- exports.createRegistryFromUIDL = createRegistryFromUIDL;
296
- //# sourceMappingURL=array-mapper-registry.js.map