@flowselections/floriday-voorraad 1.0.29 → 1.0.31

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 (47) hide show
  1. package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/StockSyncStatus.js +20 -3
  3. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  4. package/dist-lib/components/voorraad/VoorraadPage.js +1 -4
  5. package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -1
  6. package/dist-lib/hooks/useStockSyncHealth.js +7 -1
  7. package/dist-lib/integrations/supabase/auth-middleware.d.ts +9404 -6816
  8. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  9. package/dist-lib/integrations/supabase/client.d.ts +9404 -6816
  10. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  11. package/dist-lib/integrations/supabase/client.server.d.ts +9404 -6816
  12. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/types.d.ts +9658 -7030
  14. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  15. package/dist-lib/lib/floricode-required-features.functions.d.ts +9404 -6816
  16. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  17. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +9404 -6816
  18. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/floriday-env.functions.d.ts +5819 -643
  20. package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -1
  21. package/dist-lib/lib/floriday-gateway.functions.d.ts +17319 -1791
  22. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  23. package/dist-lib/lib/floriday-stock-supply.server.d.ts +53 -0
  24. package/dist-lib/lib/floriday-stock-supply.server.d.ts.map +1 -0
  25. package/dist-lib/lib/floriday-stock-supply.server.js +151 -0
  26. package/dist-lib/lib/floriday-vbn-required.functions.d.ts +5819 -643
  27. package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/floriday-warehouse.functions.d.ts +8694 -930
  29. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/floriday-writes.functions.d.ts +5819 -643
  31. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  32. package/dist-lib/lib/logistics-tasks.server.d.ts +17 -0
  33. package/dist-lib/lib/logistics-tasks.server.d.ts.map +1 -0
  34. package/dist-lib/lib/logistics-tasks.server.js +303 -0
  35. package/dist-lib/lib/module-runtime-config.functions.d.ts +1 -13
  36. package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -1
  37. package/dist-lib/lib/module-runtime-config.functions.js +17 -93
  38. package/dist-lib/lib/module-runtime.server.d.ts +33 -0
  39. package/dist-lib/lib/module-runtime.server.d.ts.map +1 -0
  40. package/dist-lib/lib/module-runtime.server.js +184 -0
  41. package/dist-lib/lib/stock-sync.functions.d.ts +20445 -0
  42. package/dist-lib/lib/stock-sync.functions.d.ts.map +1 -0
  43. package/dist-lib/lib/stock-sync.functions.js +30 -0
  44. package/dist-lib/lib/supabase-admin.server.d.ts +15 -0
  45. package/dist-lib/lib/supabase-admin.server.d.ts.map +1 -0
  46. package/dist-lib/lib/supabase-admin.server.js +35 -0
  47. package/package.json +2 -2
@@ -0,0 +1,17 @@
1
+ export declare const TASK_TYPE = "stock.reserve";
2
+ export declare const MAX_ATTEMPTS = 8;
3
+ export declare class PermanentTaskError extends Error {
4
+ readonly permanent = true;
5
+ }
6
+ export interface RunSummary {
7
+ claimed: number;
8
+ done: number;
9
+ retried: number;
10
+ failed: number;
11
+ shadow: number;
12
+ skipped: number;
13
+ durationMs: number;
14
+ }
15
+ export declare function runLogisticsTasks(): Promise<RunSummary>;
16
+ export declare function maybeTickLogisticsTasks(): void;
17
+ //# sourceMappingURL=logistics-tasks.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logistics-tasks.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-tasks.server.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,YAAY,IAAI,CAAC;AAQ9B,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,SAAS,QAAQ;CAC3B;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AA0FD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,CAuJ7D;AAgBD,wBAAgB,uBAAuB,IAAI,IAAI,CA2B9C"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Achtergrondverwerking van Logistics Engine-taken (`stock.reserve`).
3
+ *
4
+ * De Engine plant; deze module voert uit. Er is GEEN UI meer: een cron roept
5
+ * elke minuut de hook `/api/public/hooks/logistics-tasks-run` aan, die deze
6
+ * runner start.
7
+ *
8
+ * Bulletproof-eigenschappen:
9
+ * - Atomair claimen (`claim_logistics_tasks`, FOR UPDATE SKIP LOCKED), dus
10
+ * nooit dubbele uitvoering bij overlappende runs of meerdere instanties.
11
+ * - Vastgelopen taken (`in_progress` ouder dan 5 min) worden vanzelf herclaimd.
12
+ * - Exponentiële backoff met jitter bij tijdelijke fouten; permanente fouten
13
+ * stoppen direct.
14
+ * - Idempotent: reserveringen worden op een absolute waarde gezet, nooit
15
+ * opgeteld. Een herhaalde poging kan dus niet dubbeltellen.
16
+ * - Harde tijdslimiet per run zodat de worker nooit zijn limiet overschrijdt.
17
+ *
18
+ * Server-only: dit bestand mag nooit vanuit componenten geïmporteerd worden.
19
+ */
20
+ import { OWNER_MODULE, describeLoadCarriers, parseStockReservePlan, } from "./logistics-engine";
21
+ export const TASK_TYPE = "stock.reserve";
22
+ export const MAX_ATTEMPTS = 8;
23
+ const BATCH_SIZE = 25;
24
+ const STALE_MINUTES = 5;
25
+ const RUN_BUDGET_MS = 25000;
26
+ /** Wachttijd (minuten) per poging; laatste waarde geldt voor alle latere. */
27
+ const BACKOFF_MINUTES = [1, 2, 4, 8, 15, 30];
28
+ const ATTEMPT_LOG_LIMIT = 20;
29
+ export class PermanentTaskError extends Error {
30
+ constructor() {
31
+ super(...arguments);
32
+ this.permanent = true;
33
+ }
34
+ }
35
+ function backoffMs(attempt) {
36
+ const idx = Math.min(Math.max(attempt - 1, 0), BACKOFF_MINUTES.length - 1);
37
+ const base = BACKOFF_MINUTES[idx] * 60000;
38
+ // Jitter (±20%) voorkomt dat veel taken tegelijk opnieuw losbarsten.
39
+ const jitter = base * 0.2 * (Math.random() * 2 - 1);
40
+ return Math.max(15000, Math.round(base + jitter));
41
+ }
42
+ function isPermanent(error) {
43
+ if (error instanceof PermanentTaskError)
44
+ return true;
45
+ const message = String(error?.message ?? error ?? "").toLowerCase();
46
+ return (message.includes("geen voorraadrij") ||
47
+ message.includes("ongeldige payload") ||
48
+ message.includes("stale_connection") ||
49
+ message.includes("no_active_floriday_connection"));
50
+ }
51
+ function errorMessage(error) {
52
+ const raw = error?.message ?? String(error ?? "onbekende fout");
53
+ return String(raw).slice(0, 500);
54
+ }
55
+ function appendAttempt(task, entry) {
56
+ const current = Array.isArray(task.attempt_log)
57
+ ? task.attempt_log
58
+ : [];
59
+ return [...current, entry].slice(-ATTEMPT_LOG_LIMIT);
60
+ }
61
+ /**
62
+ * Voert één geclaimde taak uit. Gooit bij mislukking; de caller bepaalt
63
+ * of er opnieuw geprobeerd wordt.
64
+ */
65
+ async function executeTask(supabaseAdmin, task, connectionId) {
66
+ const plan = parseStockReservePlan(task.payload);
67
+ if (plan.lines.length === 0) {
68
+ return {
69
+ applied: false,
70
+ diff: [],
71
+ message: `Taak zonder regels (${describeLoadCarriers(plan.loadCarriers)}).`,
72
+ };
73
+ }
74
+ const keys = plan.lines.map((l) => l.tradeItemKey);
75
+ let query = supabaseAdmin
76
+ .from("trade_item_stock")
77
+ .select("trade_item_key,physical_stock,reserved_stock,connection_id")
78
+ .in("trade_item_key", keys);
79
+ if (connectionId)
80
+ query = query.eq("connection_id", connectionId);
81
+ const { data: rows, error: readError } = await query;
82
+ if (readError)
83
+ throw new Error(`voorraad lezen: ${readError.message}`);
84
+ const stock = (rows ?? []);
85
+ const diff = plan.lines.map((line) => {
86
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
87
+ return {
88
+ tradeItemKey: line.tradeItemKey,
89
+ currentReserved: row?.reserved_stock ?? 0,
90
+ engineReserved: line.quantity,
91
+ delta: line.quantity - (row?.reserved_stock ?? 0),
92
+ };
93
+ });
94
+ if (task.shadow) {
95
+ return {
96
+ applied: false,
97
+ diff,
98
+ message: `Schaduwmodus: ${diff.length} regel(s) vergeleken, niets geschreven (${describeLoadCarriers(plan.loadCarriers)}).`,
99
+ };
100
+ }
101
+ for (const line of plan.lines) {
102
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
103
+ if (!row) {
104
+ throw new PermanentTaskError(`Geen voorraadrij voor ${line.tradeItemKey}`);
105
+ }
106
+ // Absolute waarde: idempotent bij een herhaalde poging.
107
+ const { error } = await supabaseAdmin
108
+ .from("trade_item_stock")
109
+ .update({ reserved_stock: line.quantity })
110
+ .eq("connection_id", row.connection_id)
111
+ .eq("trade_item_key", line.tradeItemKey);
112
+ if (error)
113
+ throw new Error(`reservering ${line.tradeItemKey}: ${error.message}`);
114
+ }
115
+ return {
116
+ applied: true,
117
+ diff,
118
+ message: `${plan.lines.length} regel(s) gereserveerd (${describeLoadCarriers(plan.loadCarriers)}).`,
119
+ };
120
+ }
121
+ export async function runLogisticsTasks() {
122
+ const startedAt = Date.now();
123
+ const summary = {
124
+ claimed: 0,
125
+ done: 0,
126
+ retried: 0,
127
+ failed: 0,
128
+ shadow: 0,
129
+ skipped: 0,
130
+ durationMs: 0,
131
+ };
132
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
133
+ // Actieve koppeling is optioneel: zonder koppeling reserveren we op alle
134
+ // rijen van het artikel. Faalt de resolutie, dan blokkeren we niet.
135
+ let connectionId = null;
136
+ try {
137
+ const { resolveActiveFloridayConnection } = await import("./floriday-env.server");
138
+ connectionId = (await resolveActiveFloridayConnection(supabaseAdmin)).id;
139
+ }
140
+ catch {
141
+ connectionId = null;
142
+ }
143
+ const worker = `voorraad-${crypto.randomUUID().slice(0, 8)}`;
144
+ const { data: claimed, error: claimError } = await supabaseAdmin.rpc("claim_logistics_tasks", {
145
+ _owner_module: OWNER_MODULE,
146
+ _task_type: TASK_TYPE,
147
+ _worker: worker,
148
+ _limit: BATCH_SIZE,
149
+ _stale_minutes: STALE_MINUTES,
150
+ });
151
+ if (claimError)
152
+ throw new Error(`taken claimen: ${claimError.message}`);
153
+ const tasks = (claimed ?? []);
154
+ summary.claimed = tasks.length;
155
+ for (const task of tasks) {
156
+ if (Date.now() - startedAt > RUN_BUDGET_MS) {
157
+ // Tijdbudget op: taak teruggeven zodat de volgende run hem oppakt.
158
+ await supabaseAdmin
159
+ .from("logistics_tasks")
160
+ .update({
161
+ status: "planned",
162
+ claimed_by: null,
163
+ claimed_at: null,
164
+ next_attempt_at: new Date().toISOString(),
165
+ updated_at: new Date().toISOString(),
166
+ })
167
+ .eq("id", task.id)
168
+ .eq("claimed_by", worker);
169
+ summary.skipped++;
170
+ continue;
171
+ }
172
+ const attempt = (task.attempts ?? 0) + 1;
173
+ const nowIso = new Date().toISOString();
174
+ try {
175
+ const result = await executeTask(supabaseAdmin, task, connectionId);
176
+ const log = appendAttempt(task, {
177
+ attempt,
178
+ at: nowIso,
179
+ ok: true,
180
+ });
181
+ await supabaseAdmin
182
+ .from("logistics_tasks")
183
+ .update({
184
+ status: "done",
185
+ attempts: attempt,
186
+ attempt_log: log,
187
+ last_error: null,
188
+ error: null,
189
+ next_attempt_at: null,
190
+ claimed_by: null,
191
+ claimed_at: null,
192
+ result: {
193
+ taskId: task.id,
194
+ applied: result.applied,
195
+ shadow: task.shadow,
196
+ diff: result.diff,
197
+ message: result.message,
198
+ },
199
+ completed_at: nowIso,
200
+ updated_at: nowIso,
201
+ })
202
+ .eq("id", task.id);
203
+ if (task.shadow)
204
+ summary.shadow++;
205
+ summary.done++;
206
+ }
207
+ catch (e) {
208
+ const message = errorMessage(e);
209
+ const permanent = isPermanent(e);
210
+ const exhausted = attempt >= MAX_ATTEMPTS;
211
+ const log = appendAttempt(task, {
212
+ attempt,
213
+ at: nowIso,
214
+ ok: false,
215
+ error: message,
216
+ permanent,
217
+ });
218
+ if (permanent || exhausted) {
219
+ await supabaseAdmin
220
+ .from("logistics_tasks")
221
+ .update({
222
+ status: "failed",
223
+ attempts: attempt,
224
+ attempt_log: log,
225
+ last_error: message,
226
+ error: message,
227
+ next_attempt_at: null,
228
+ claimed_by: null,
229
+ claimed_at: null,
230
+ completed_at: nowIso,
231
+ updated_at: nowIso,
232
+ })
233
+ .eq("id", task.id);
234
+ summary.failed++;
235
+ console.error(`[voorraad] taak ${task.id} definitief mislukt (poging ${attempt}): ${message}`);
236
+ }
237
+ else {
238
+ const next = new Date(Date.now() + backoffMs(attempt)).toISOString();
239
+ await supabaseAdmin
240
+ .from("logistics_tasks")
241
+ .update({
242
+ status: "planned",
243
+ attempts: attempt,
244
+ attempt_log: log,
245
+ last_error: message,
246
+ error: message,
247
+ next_attempt_at: next,
248
+ claimed_by: null,
249
+ claimed_at: null,
250
+ updated_at: nowIso,
251
+ })
252
+ .eq("id", task.id);
253
+ summary.retried++;
254
+ console.warn(`[voorraad] taak ${task.id} mislukt (poging ${attempt}), opnieuw om ${next}: ${message}`);
255
+ }
256
+ }
257
+ }
258
+ summary.durationMs = Date.now() - startedAt;
259
+ return summary;
260
+ }
261
+ /**
262
+ * Vangnet zonder cron.
263
+ *
264
+ * Kan pg_net de host (nog) niet bereiken — bijvoorbeeld direct nadat de module
265
+ * in een nieuwe shell is gehangen — dan blijven taken staan. Deze tick wordt
266
+ * fire-and-forget vanuit binnenkomende requests aangeroepen: hoogstens één keer
267
+ * per minuut, en alleen wanneer er daadwerkelijk taken openstaan.
268
+ * Volledig onzichtbaar voor de gebruiker en veilig, omdat de runner atomair
269
+ * claimt en idempotent is.
270
+ */
271
+ const TICK_THROTTLE_MS = 60000;
272
+ let lastTickAt = 0;
273
+ let tickInFlight = false;
274
+ export function maybeTickLogisticsTasks() {
275
+ const now = Date.now();
276
+ if (tickInFlight || now - lastTickAt < TICK_THROTTLE_MS)
277
+ return;
278
+ lastTickAt = now;
279
+ tickInFlight = true;
280
+ void (async () => {
281
+ try {
282
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
283
+ const { count, error } = await supabaseAdmin
284
+ .from("logistics_tasks")
285
+ .select("id", { count: "exact", head: true })
286
+ .eq("owner_module", OWNER_MODULE)
287
+ .eq("task_type", TASK_TYPE)
288
+ .eq("status", "planned")
289
+ .or(`next_attempt_at.is.null,next_attempt_at.lte.${new Date().toISOString()}`);
290
+ if (error)
291
+ throw new Error(error.message);
292
+ if (!count)
293
+ return;
294
+ await runLogisticsTasks();
295
+ }
296
+ catch (e) {
297
+ console.warn("[voorraad] achtergrond-tick mislukt:", e?.message ?? e);
298
+ }
299
+ finally {
300
+ tickInFlight = false;
301
+ }
302
+ })();
303
+ }
@@ -1,14 +1,2 @@
1
- export interface RegisterBaseUrlResult {
2
- ok: boolean;
3
- baseUrl: string | null;
4
- changed: boolean;
5
- reason?: string;
6
- }
7
- /**
8
- * Idempotent: schrijft alleen wanneer de waarde daadwerkelijk wijzigt.
9
- * Bewust zonder auth-middleware zodat de registratie ook tijdens SSR van een
10
- * publieke shell-pagina kan slagen; er wordt niets gelezen of teruggegeven
11
- * dat gevoelig is.
12
- */
13
- export declare const registerModuleBaseUrl: import("@tanstack/start-client-core").OptionalFetcher<undefined, undefined, Promise<RegisterBaseUrlResult>>;
1
+ export declare const registerModuleBaseUrl: import("@tanstack/start-client-core").OptionalFetcher<undefined, undefined, Promise<import("./module-runtime.server").ModuleRegistrationResult>>;
14
2
  //# sourceMappingURL=module-runtime-config.functions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module-runtime-config.functions.d.ts","sourceRoot":"","sources":["../../src/lib/module-runtime-config.functions.ts"],"names":[],"mappings":"AAsEA,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,6GAqCjC,CAAC"}
1
+ {"version":3,"file":"module-runtime-config.functions.d.ts","sourceRoot":"","sources":["../../src/lib/module-runtime-config.functions.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB,kJAsBjC,CAAC"}
@@ -1,107 +1,31 @@
1
1
  /**
2
- * Zelf-registrerende basis-URL voor deze module.
2
+ * Server-functie die de module registreert in de database waarin hij draait.
3
3
  *
4
- * De cron-taak `run_floriday_continuous_stock_sync()` in Postgres bevat GEEN
5
- * hardgecodeerde host: hij leest `module_runtime_config.voorraad_base_url`.
6
- * Deze server-functie schrijft die waarde weg met de origin van de shell
7
- * waarin de module op dat moment draait, zodat de voorraadsync blijft werken
8
- * wanneer de module in een andere shell wordt gehangen.
9
- *
10
- * Resolutievolgorde:
11
- * 1. PUBLIC_BASE_URL (expliciet gezet)
12
- * 2. centrale Auth-module `/api/shell-host`
13
- * 3. origin van het inkomende request
4
+ * De echte logica staat in `module-runtime.server.ts`. Deze wrapper wordt
5
+ * tijdens SSR aangeroepen (zie `src/routes/__root.tsx`), zodat de cron-hosts
6
+ * kloppen zodra de module in een andere shell wordt gehangen — zonder UI en
7
+ * zonder handmatige instellingen.
14
8
  */
15
9
  import { createServerFn } from "@tanstack/react-start";
16
10
  import { getRequest } from "@tanstack/react-start/server";
17
- const CONFIG_KEY = "voorraad_base_url";
18
- function normalizeOrigin(raw) {
19
- const trimmed = String(raw ?? "").trim();
20
- if (!trimmed)
21
- return null;
22
- try {
23
- const u = new URL(trimmed);
24
- if (u.protocol !== "https:" && u.protocol !== "http:")
25
- return null;
26
- // Lokale dev-origins nooit registreren: pg_cron kan die niet bereiken.
27
- const host = u.hostname.toLowerCase();
28
- if (host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host.endsWith(".local")) {
29
- return null;
30
- }
31
- return `${u.protocol}//${u.host}`;
32
- }
33
- catch {
34
- return null;
35
- }
36
- }
37
- async function resolveBaseUrl() {
38
- const explicit = normalizeOrigin(process.env.PUBLIC_BASE_URL);
39
- if (explicit)
40
- return explicit;
41
- // De hooks (/api/public/hooks/...) worden door DEZE module geserveerd,
42
- // dus de origin van het inkomende request heeft voorrang op de shell-host.
11
+ export const registerModuleBaseUrl = createServerFn({ method: "POST" }).handler(async () => {
12
+ const { registerModuleRuntime } = await import("./module-runtime.server");
13
+ let request = null;
43
14
  try {
44
- const req = getRequest();
45
- const fromRequest = normalizeOrigin(req?.url ?? null);
46
- if (fromRequest)
47
- return fromRequest;
48
- const forwardedHost = req?.headers?.get("x-forwarded-host") ?? req?.headers?.get("host");
49
- if (forwardedHost) {
50
- const proto = req?.headers?.get("x-forwarded-proto") ?? "https";
51
- const fromHeaders = normalizeOrigin(`${proto}://${forwardedHost}`);
52
- if (fromHeaders)
53
- return fromHeaders;
54
- }
15
+ request = getRequest();
55
16
  }
56
17
  catch {
57
- // geen request-context
18
+ request = null;
58
19
  }
20
+ const result = await registerModuleRuntime(request);
21
+ // Vangnet: mocht de cron (nog) niet aankomen, dan verwerkt de module zelf
22
+ // openstaande taken. Throttled tot maximaal één run per minuut.
59
23
  try {
60
- const { getShellHost } = await import("./centralSecrets.server");
61
- const host = normalizeOrigin(await getShellHost());
62
- if (host)
63
- return host;
24
+ const { maybeTickLogisticsTasks } = await import("./logistics-tasks.server");
25
+ maybeTickLogisticsTasks();
64
26
  }
65
27
  catch {
66
- // centrale module niet bereikbaar
67
- }
68
- return null;
69
- }
70
- /**
71
- * Idempotent: schrijft alleen wanneer de waarde daadwerkelijk wijzigt.
72
- * Bewust zonder auth-middleware zodat de registratie ook tijdens SSR van een
73
- * publieke shell-pagina kan slagen; er wordt niets gelezen of teruggegeven
74
- * dat gevoelig is.
75
- */
76
- export const registerModuleBaseUrl = createServerFn({ method: "POST" }).handler(async () => {
77
- const baseUrl = await resolveBaseUrl();
78
- if (!baseUrl) {
79
- return { ok: false, baseUrl: null, changed: false, reason: "geen origin resolvbaar" };
80
- }
81
- try {
82
- const { supabaseAdmin } = await import("../integrations/supabase/client.server");
83
- const { data: existing } = await supabaseAdmin
84
- .from("module_runtime_config")
85
- .select("value")
86
- .eq("key", CONFIG_KEY)
87
- .maybeSingle();
88
- if (existing?.value === baseUrl) {
89
- return { ok: true, baseUrl, changed: false };
90
- }
91
- const { error } = await supabaseAdmin
92
- .from("module_runtime_config")
93
- .upsert({ key: CONFIG_KEY, value: baseUrl }, { onConflict: "key" });
94
- if (error) {
95
- return { ok: false, baseUrl, changed: false, reason: error.message };
96
- }
97
- return { ok: true, baseUrl, changed: true };
98
- }
99
- catch (e) {
100
- return {
101
- ok: false,
102
- baseUrl,
103
- changed: false,
104
- reason: e?.message ?? String(e),
105
- };
28
+ // tick mag nooit de registratie laten falen
106
29
  }
30
+ return result;
107
31
  });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Zelfregistratie van de module in de database waarin hij draait.
3
+ *
4
+ * De cron-functies `run_logistics_tasks_dispatch()` en
5
+ * `run_floriday_continuous_stock_sync()` bevatten geen hardgecodeerde host:
6
+ * ze lezen `module_runtime_config.voorraad_base_url` en
7
+ * `module_runtime_config.voorraad_hook_apikey`. Dit bestand schrijft die twee
8
+ * waarden weg zodra de module ergens een request afhandelt, zodat de cron
9
+ * out-of-the-box werkt in elke shell/database waar de module wordt gehangen —
10
+ * zonder handmatige instellingen en zonder iets in de UI.
11
+ *
12
+ * Server-only: nooit importeren vanuit componenten of client-code.
13
+ */
14
+ /**
15
+ * Resolutie met verificatie: de eerste kandidaat die de module daadwerkelijk
16
+ * bedient wint. Bedient geen enkele kandidaat de module, dan wordt er niets
17
+ * geregistreerd (de bestaande, werkende waarde blijft staan).
18
+ */
19
+ export declare function resolveModuleBaseUrl(request?: Request | null): Promise<string | null>;
20
+ export interface ModuleRegistrationResult {
21
+ ok: boolean;
22
+ baseUrl: string | null;
23
+ changed: boolean;
24
+ reason?: string;
25
+ }
26
+ /** Schrijft basis-URL + hook-apikey weg (idempotent, schrijft alleen bij wijziging). */
27
+ export declare function registerModuleRuntime(request?: Request | null): Promise<ModuleRegistrationResult>;
28
+ /**
29
+ * Fire-and-forget variant met throttle; veilig om bij élk request aan te roepen.
30
+ * Faalt nooit hard en logt alleen bij een echte fout.
31
+ */
32
+ export declare function ensureModuleRuntimeRegistered(request?: Request | null): void;
33
+ //# sourceMappingURL=module-runtime.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-runtime.server.d.ts","sourceRoot":"","sources":["../../src/lib/module-runtime.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2FH;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAkCD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,wBAAsB,qBAAqB,CACzC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,wBAAwB,CAAC,CAyBnC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAsB5E"}