@flowselections/floriday-authenticatie-module 1.0.14 → 1.0.16

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 (35) hide show
  1. package/dist-lib/components/settings/FlorydaySettingsCard.d.ts.map +1 -1
  2. package/dist-lib/components/settings/FlorydaySettingsCard.js +2 -2
  3. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1486 -35
  4. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  5. package/dist-lib/integrations/supabase/client.d.ts +1486 -35
  6. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  7. package/dist-lib/integrations/supabase/client.server.d.ts +1486 -35
  8. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  9. package/dist-lib/integrations/supabase/types.d.ts +1515 -34
  10. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  11. package/dist-lib/lib/floricode-product-features.d.ts.map +1 -1
  12. package/dist-lib/lib/floricode-product-features.js +59 -5
  13. package/dist-lib/lib/floricode-regulatory-features.d.ts +15 -0
  14. package/dist-lib/lib/floricode-regulatory-features.d.ts.map +1 -1
  15. package/dist-lib/lib/floricode-regulatory-features.js +77 -21
  16. package/dist-lib/lib/floriday-auction-letters.functions.d.ts +1486 -35
  17. package/dist-lib/lib/floriday-auction-letters.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/floriday-labels.functions.d.ts +1486 -35
  19. package/dist-lib/lib/floriday-labels.functions.d.ts.map +1 -1
  20. package/dist-lib/lib/floriday-public.functions.d.ts +8706 -0
  21. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  22. package/dist-lib/lib/floriday-scopes.d.ts +17 -1
  23. package/dist-lib/lib/floriday-scopes.d.ts.map +1 -1
  24. package/dist-lib/lib/floriday-scopes.js +66 -5
  25. package/dist-lib/lib/floriday-stickers.functions.d.ts +1486 -35
  26. package/dist-lib/lib/floriday-stickers.functions.d.ts.map +1 -1
  27. package/dist-lib/lib/floriday-sync.functions.d.ts +66486 -1
  28. package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
  29. package/dist-lib/lib/floriday-sync.functions.js +92 -2
  30. package/dist-lib/lib/floriday-writes.functions.d.ts +8724 -6
  31. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  32. package/dist-lib/lib/floriday.functions.d.ts +26244 -144
  33. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  34. package/dist-lib/lib/floriday.functions.js +13 -2
  35. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"floricode-product-features.d.ts","sourceRoot":"","sources":["../../src/lib/floricode-product-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAShE,QAAA,MAAM,yBAAyB,+BAA+B,CAAC;AAY/D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AA0OD,wBAAsB,mCAAmC,CACvD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,mBAAmB,EAChC,gBAAgB,EAAE,MAAM,GAAG,MAAM,EACjC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC;IACT,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACnC,YAAY,EAAE,OAAO,yBAAyB,CAAC;IAC/C,eAAe,EAAE,qBAAqB,EAAE,CAAC;CAC1C,CAAC,CAuDD;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;GAiF1D"}
1
+ {"version":3,"file":"floricode-product-features.d.ts","sourceRoot":"","sources":["../../src/lib/floricode-product-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAShE,QAAA,MAAM,yBAAyB,+BAA+B,CAAC;AAc/D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAuRD,wBAAsB,mCAAmC,CACvD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,mBAAmB,EAChC,gBAAgB,EAAE,MAAM,GAAG,MAAM,EACjC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC;IACT,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACnC,YAAY,EAAE,OAAO,yBAAyB,CAAC;IAC/C,eAAe,EAAE,qBAAqB,EAAE,CAAC;CAC1C,CAAC,CAiFD;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;GAiF1D"}
@@ -4,6 +4,8 @@ const FLORICODE_PRODUCT_FEATURE_URL = "https://api.floricode.com/v2/VBN/ProductF
4
4
  const PRODUCT_FEATURE_SCOPE = "vbn_productfeature:read";
5
5
  const PRODUCT_FEATURE_LIST_NAME = "vbn-product-characteristic";
6
6
  const PRODUCT_FEATURE_RESOURCE = "ProductFeature";
7
+ const REG_FEATURE_TYPE_RESOURCE = "RegulatoryFeatureType";
8
+ const SENTINEL_NONE_MARKER = "__none__";
7
9
  function normalizeProductCode(input) {
8
10
  const value = String(input).trim();
9
11
  if (!/^\d+$/.test(value)) {
@@ -191,11 +193,41 @@ async function readCachedProductCharacteristics(supabase, environment, productCo
191
193
  });
192
194
  return rows;
193
195
  }
196
+ function cacheIsComplete(cached) {
197
+ // Volledige cache-hit = er staat minimaal één regulatory-feature (S-code) in.
198
+ // Puur B*/T*-rijen zonder regulation_type_id betekent dat de RegulatoryFeatureType-
199
+ // sync dit product nog niet heeft geraakt (bulk-sync nog niet klaar of nieuwe koppeling).
200
+ return cached.some((row) => row.regulationTypeId != null ||
201
+ String(row.payload?.source ?? "") === "Floricode RegulatoryFeatureType");
202
+ }
203
+ async function hasSentinelForProduct(supabase, productCode) {
204
+ const { data } = await supabase
205
+ .from("floricode_records")
206
+ .select("record_id")
207
+ .eq("resource", REG_FEATURE_TYPE_RESOURCE)
208
+ .eq("record_id", `${productCode}:${SENTINEL_NONE_MARKER}`)
209
+ .maybeSingle();
210
+ return !!data;
211
+ }
212
+ async function writeSentinelForProduct(supabase, productCode) {
213
+ const nowIso = new Date().toISOString();
214
+ await supabase.from("floricode_records").upsert({
215
+ resource: REG_FEATURE_TYPE_RESOURCE,
216
+ record_id: `${productCode}:${SENTINEL_NONE_MARKER}`,
217
+ data: {
218
+ product_id: productCode,
219
+ __sentinel: "no-regulatory-features",
220
+ checked_at: nowIso,
221
+ },
222
+ change_date_time: null,
223
+ last_synced_at: nowIso,
224
+ }, { onConflict: "resource,record_id" });
225
+ }
194
226
  export async function getProductCharacteristicsForVbnCode(supabase, environment, productCodeInput, options = {}) {
195
227
  const productCode = normalizeProductCode(productCodeInput);
196
228
  if (!options.forceRefresh) {
197
229
  const cached = await readCachedProductCharacteristics(supabase, environment, productCode);
198
- if (cached.length > 0) {
230
+ if (cached.length > 0 && cacheIsComplete(cached)) {
199
231
  return {
200
232
  ok: true,
201
233
  productCode,
@@ -206,10 +238,25 @@ export async function getProductCharacteristicsForVbnCode(supabase, environment,
206
238
  characteristics: cached,
207
239
  };
208
240
  }
241
+ // Sentinel: eerder bevestigd dat dit product geen regulatory features heeft.
242
+ // Terugvallen op wat er in de cache staat (B*/T* of leeg) zonder Floricode-call.
243
+ if (await hasSentinelForProduct(supabase, productCode)) {
244
+ const meta = await loadProductMeta(supabase, environment, productCode);
245
+ return {
246
+ ok: true,
247
+ productCode,
248
+ productGroupId: cached[0]?.productGroupId ?? meta.productGroupId,
249
+ productName: cached[0]?.productName ?? meta.productName,
250
+ source: "cache",
251
+ codeListName: PRODUCT_FEATURE_LIST_NAME,
252
+ characteristics: cached,
253
+ };
254
+ }
209
255
  }
210
- // Live-fetch: RegulatoryFeatureType (verplichte/optionele kenmerken) +
211
- // ProductFeature (default-waardes) parallel, upsert in floricode_records
212
- // en daarna RPC draaien zodat de derived code-list weer up-to-date is.
256
+ // Live-fetch: RegulatoryFeatureType (verplichte kenmerken, S-codes) + ProductFeature
257
+ // (default-waarden) parallel, upsert in floricode_records en projectie draaien.
258
+ // ProductFeature is meestal al gedekt door de bulk-sync, maar we halen 'm veiligheidshalve
259
+ // ook op zodat een nieuw product direct default-waarden krijgt.
213
260
  const [regItems, pfItems] = await Promise.all([
214
261
  fetchFloricodeRegulatoryFeaturesForProduct(productCode).catch((e) => {
215
262
  console.warn("[product-characteristics] RegulatoryFeatureType fetch failed:", String(e?.message ?? e));
@@ -225,11 +272,18 @@ export async function getProductCharacteristicsForVbnCode(supabase, environment,
225
272
  if (pfItems.length > 0)
226
273
  await upsertFloricodeProductFeatureRecords(supabase, pfItems);
227
274
  if (regItems.length > 0 || pfItems.length > 0) {
228
- const { error: projError } = await supabase.rpc("refresh_floricode_derived_code_lists");
275
+ // Lichtgewicht per-product projectie i.p.v. de volledige RPC — die is te zwaar
276
+ // om synchroon vanuit een wizard-request te draaien.
277
+ const { error: projError } = await supabase.rpc("refresh_floricode_derived_for_product", { p_product_code: productCode });
229
278
  if (projError) {
230
279
  console.warn("[product-characteristics] projection failed:", projError.message);
231
280
  }
232
281
  }
282
+ // Geen S-codes uit Floricode → sentinel schrijven zodat we dit product niet elke
283
+ // wizard-open opnieuw hoeven te bevragen.
284
+ if (regItems.length === 0) {
285
+ await writeSentinelForProduct(supabase, productCode).catch((e) => console.warn("[product-characteristics] sentinel write failed:", String(e?.message ?? e)));
286
+ }
233
287
  const fresh = await readCachedProductCharacteristics(supabase, environment, productCode);
234
288
  const meta = await loadProductMeta(supabase, environment, productCode);
235
289
  return {
@@ -28,14 +28,29 @@ export declare function syncFloricodeRegulationTypes(supabase: SupabaseClient):
28
28
  export declare function syncFloricodeRegulatoryFeatures(supabase: SupabaseClient, options?: {
29
29
  batchSize?: number;
30
30
  maxBatches?: number;
31
+ resume?: boolean;
31
32
  }): Promise<{
32
33
  ok: true;
33
34
  resource: string;
35
+ completed: boolean;
36
+ startSkip: number;
37
+ endSkip: number;
34
38
  fetched: number;
35
39
  upserted: number;
36
40
  batches: number;
37
41
  projected: any;
38
42
  durationMs: number;
43
+ } | {
44
+ ok: true;
45
+ resource: string;
46
+ completed: boolean;
47
+ startSkip: number;
48
+ endSkip: number;
49
+ fetched: number;
50
+ upserted: number;
51
+ batches: number;
52
+ durationMs: number;
53
+ projected?: undefined;
39
54
  }>;
40
55
  export {};
41
56
  //# sourceMappingURL=floricode-regulatory-features.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"floricode-regulatory-features.d.ts","sourceRoot":"","sources":["../../src/lib/floricode-regulatory-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAY5D,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAoCD,wBAAsB,0CAA0C,CAC9D,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,EAAE,CAAC,CAgB3B;AAkCD,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAiBD,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,cAAc;;;;;;GA2B1E;AAED,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;GAyE1D"}
1
+ {"version":3,"file":"floricode-regulatory-features.d.ts","sourceRoot":"","sources":["../../src/lib/floricode-regulatory-features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAY5D,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAoCD,wBAAsB,0CAA0C,CAC9D,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,EAAE,CAAC,CAgB3B;AAkCD,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,cAAc,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAiBD,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,cAAc;;;;;;GA2B1E;AAED,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;GAwI5E"}
@@ -157,22 +157,40 @@ export async function syncFloricodeRegulatoryFeatures(supabase, options = {}) {
157
157
  const startedAt = Date.now();
158
158
  const batchSize = Math.min(Math.max(options.batchSize ?? 5000, 1), 5000);
159
159
  const maxBatches = options.maxBatches && options.maxBatches > 0 ? options.maxBatches : Infinity;
160
+ const resume = options.resume !== false;
161
+ // Read cursor from state so we can resume where the previous invocation stopped.
162
+ let skip = 0;
163
+ let maxChangeDateTime = null;
164
+ if (resume) {
165
+ const { data: state } = await supabase
166
+ .from("floricode_sync_state")
167
+ .select("cursor_skip,last_change_date_time,status")
168
+ .eq("resource", REG_FEATURE_TYPE_RESOURCE)
169
+ .maybeSingle();
170
+ if (state && state.status === "running") {
171
+ skip = Number(state.cursor_skip ?? 0) || 0;
172
+ maxChangeDateTime = state.last_change_date_time ?? null;
173
+ }
174
+ }
175
+ const startSkip = skip;
160
176
  let fetched = 0;
161
177
  let upserted = 0;
162
- let skip = 0;
163
178
  let batches = 0;
164
- let maxChangeDateTime = null;
179
+ let completed = false;
165
180
  await setSyncState(supabase, REG_FEATURE_TYPE_RESOURCE, {
166
181
  status: "running",
167
182
  error_message: null,
183
+ cursor_skip: skip,
168
184
  last_sync_at: new Date().toISOString(),
169
185
  });
170
186
  try {
171
187
  const token = await getFloricodeToken(REG_FEATURE_TYPE_SCOPE);
172
188
  while (batches < maxBatches) {
173
189
  const items = await fetchRegFeatureTypePage(token, skip, batchSize);
174
- if (items.length === 0)
190
+ if (items.length === 0) {
191
+ completed = true;
175
192
  break;
193
+ }
176
194
  fetched += items.length;
177
195
  upserted += await upsertRegulatoryFeatureRecords(supabase, items);
178
196
  for (const item of items) {
@@ -182,33 +200,71 @@ export async function syncFloricodeRegulatoryFeatures(supabase, options = {}) {
182
200
  }
183
201
  batches += 1;
184
202
  skip += items.length;
185
- if (items.length < batchSize)
203
+ // Persist cursor after each batch so a crash mid-run resumes correctly.
204
+ await setSyncState(supabase, REG_FEATURE_TYPE_RESOURCE, {
205
+ status: "running",
206
+ error_message: null,
207
+ cursor_skip: skip,
208
+ last_change_date_time: maxChangeDateTime,
209
+ last_sync_at: new Date().toISOString(),
210
+ });
211
+ if (items.length < batchSize) {
212
+ completed = true;
186
213
  break;
214
+ }
187
215
  }
188
- // RegulationType is een kleine lijst — altijd meeladen voor labels.
189
- try {
190
- await syncFloricodeRegulationTypes(supabase);
216
+ // Ook bij partial runs projectie draaien zodat vbn-product-characteristic
217
+ // meegroeit met de raw cache. Fouten hier mogen de cursor niet blokkeren.
218
+ if (!completed && upserted > 0) {
219
+ try {
220
+ await supabase.rpc("refresh_floricode_derived_code_lists");
221
+ }
222
+ catch (e) {
223
+ console.warn("[regulatory] partial projection skipped:", String(e?.message ?? e));
224
+ }
191
225
  }
192
- catch (e) {
193
- console.warn("[regulatory] RegulationType sync skipped:", String(e?.message ?? e));
226
+ if (completed) {
227
+ // RegulationType is een kleine lijst — altijd meeladen voor labels.
228
+ try {
229
+ await syncFloricodeRegulationTypes(supabase);
230
+ }
231
+ catch (e) {
232
+ console.warn("[regulatory] RegulationType sync skipped:", String(e?.message ?? e));
233
+ }
234
+ const { data: projection, error: projectionError } = await supabase.rpc("refresh_floricode_derived_code_lists");
235
+ if (projectionError)
236
+ throw new Error(`Projectie naar code-lists mislukt: ${projectionError.message}`);
237
+ await setSyncState(supabase, REG_FEATURE_TYPE_RESOURCE, {
238
+ status: "success",
239
+ error_message: null,
240
+ cursor_skip: 0,
241
+ last_sequence_number: skip,
242
+ last_change_date_time: maxChangeDateTime,
243
+ last_sync_at: new Date().toISOString(),
244
+ });
245
+ return {
246
+ ok: true,
247
+ resource: REG_FEATURE_TYPE_RESOURCE,
248
+ completed: true,
249
+ startSkip,
250
+ endSkip: skip,
251
+ fetched,
252
+ upserted,
253
+ batches,
254
+ projected: projection ?? [],
255
+ durationMs: Date.now() - startedAt,
256
+ };
194
257
  }
195
- const { data: projection, error: projectionError } = await supabase.rpc("refresh_floricode_derived_code_lists");
196
- if (projectionError)
197
- throw new Error(`Projectie naar code-lists mislukt: ${projectionError.message}`);
198
- await setSyncState(supabase, REG_FEATURE_TYPE_RESOURCE, {
199
- status: "success",
200
- error_message: null,
201
- last_sequence_number: fetched,
202
- last_change_date_time: maxChangeDateTime,
203
- last_sync_at: new Date().toISOString(),
204
- });
258
+ // Partial run — leave status='running' so the next cron tick resumes.
205
259
  return {
206
260
  ok: true,
207
261
  resource: REG_FEATURE_TYPE_RESOURCE,
262
+ completed: false,
263
+ startSkip,
264
+ endSkip: skip,
208
265
  fetched,
209
266
  upserted,
210
267
  batches,
211
- projected: projection ?? [],
212
268
  durationMs: Date.now() - startedAt,
213
269
  };
214
270
  }
@@ -217,7 +273,7 @@ export async function syncFloricodeRegulatoryFeatures(supabase, options = {}) {
217
273
  await setSyncState(supabase, REG_FEATURE_TYPE_RESOURCE, {
218
274
  status: "error",
219
275
  error_message: msg,
220
- last_sequence_number: fetched,
276
+ cursor_skip: skip,
221
277
  last_change_date_time: maxChangeDateTime,
222
278
  last_sync_at: new Date().toISOString(),
223
279
  });