@flowselections/core 1.0.30 → 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.
package/README.md CHANGED
@@ -213,7 +213,46 @@ De door het dashboard gegenereerde globale request-middleware roept per verzoek
213
213
 
214
214
  ```ts
215
215
  import { runShellRuntime } from "@flowselections/core/server";
216
- await runShellRuntime(); // zet PUBLIC_BASE_URL uit de database, controleert de Central-koppeling hooguit eens per uur
216
+ await runShellRuntime(); // bestaand gedrag, geeft null terug; geen croncontrole zonder context
217
217
  ```
218
218
 
219
219
  Vereist `SUPABASE_URL`, `SB_SERVICE_ROLE_KEY`, `CENTRAL_AUTH_URL`, `CENTRAL_AUTH_SECRET`. Gooit nooit; de leessleutel wordt nooit gelogd of teruggegeven.
220
+
221
+ ### Centrale croncontrole (dashboardaansluiting)
222
+
223
+ Het dashboard past `system-files.js`, `buildBody`, voor het systeembestand
224
+ `src/lib/shell-runtime-middleware.ts` aan. Houd deze middleware als eerste:
225
+
226
+ ```ts
227
+ createMiddleware({ type: "request" }).server(async ({ request, pathname, next }) => {
228
+ const { runShellRuntime } = await import("@flowselections/core/server");
229
+ const denied = await runShellRuntime({ request, pathname }); return denied ?? next();
230
+ });
231
+ ```
232
+
233
+ `ensureCronKeys()` synchroniseert `SUPABASE_PUBLISHABLE_KEY ?? SUPABASE_ANON_KEY`
234
+ (zonder omringende aanhalingstekens) via `flowselections_set_anon_key`.
235
+ Central-koppeling en sleutelsynchronisatie wachten één uur na succes en vijf
236
+ minuten na een mislukking, per isolate. Geen sleutels in logs of responses.
237
+
238
+ `checkCronRequest(request)` controleert uitsluitend exacte paden uit
239
+ `flowselections_cron_paths()`, met `x-flowselections-cron-key` tegen
240
+ `flowselections_cron_secret()`. Native Web Crypto verifieert de vergelijking.
241
+ Paden, sleutel en configuratie worden vijf minuten gecachet; een afwijkende
242
+ sleutel veroorzaakt één herlezing vóór de beslissing. Gelijktijdige herlezingen
243
+ delen een Promise; daarna blijven herhaalde mismatches tot de volgende cacheverversing
244
+ zonder extra RPC's, om een verzoekenstorm te voorkomen. Ontbrekende RPC's, netwerkfouten of ongeldige antwoorden laten
245
+ verzoeken door en worden begrensd gemeld; deze laag vervangt geen modulebeveiliging.
246
+
247
+ `module_runtime_config` heeft `key`/`value`-rijen: `shell_base_url`,
248
+ `cron_auth_mode` (`compat` standaard, of `afdwingen`) en
249
+ `cron_auth_exempt_paths` (komma-gescheiden extra padprefixen). In `compat` komt
250
+ alleen een waarschuwing; in `afdwingen` een 401 vóór de handler. De drie vaste
251
+ uitzonderingsprefixen blijven altijd gelden, ook bij een lege configuratie:
252
+
253
+ - `/api/public/print/queue-tick`
254
+ - `/api/public/hooks/logistics-shipping-note-now`
255
+ - `/api/public/cron/ping`
256
+
257
+ De eigen engine-tick en `verkoop_cron_post` staan buiten deze centrale padenlijst.
258
+ Er worden geen requestheaders herschreven en geen modules gewijzigd.
@@ -0,0 +1,17 @@
1
+ /** URL metadata is only a correlation signal; Supabase validates the session. */
2
+ export type PasswordCallback = {
3
+ type: "invite" | "recovery";
4
+ sub: string;
5
+ };
6
+ export declare const PASSWORD_CALLBACK_WAIT_MS = 5000;
7
+ export declare function readPasswordCallback(hash: string): PasswordCallback | null;
8
+ /** Called immediately before createClient can consume/clear the URL hash. */
9
+ export declare function capturePasswordCallback(): void;
10
+ export declare function getPasswordCallback(): PasswordCallback | null;
11
+ export declare function clearPasswordCallback(): void;
12
+ export declare function matchingPasswordCallback(signal: PasswordCallback | null, session: {
13
+ user: {
14
+ id: string;
15
+ };
16
+ } | null): PasswordCallback["type"] | null;
17
+ //# sourceMappingURL=password-callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-callback.d.ts","sourceRoot":"","sources":["../../src/core/auth/password-callback.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAG9C,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAiB1E;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAC/B,OAAO,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GACvC,gBAAgB,CAAC,MAAM,CAAC,GAAG,IAAI,CAEjC"}
@@ -0,0 +1,39 @@
1
+ export const PASSWORD_CALLBACK_WAIT_MS = 5000;
2
+ let callback = null;
3
+ export function readPasswordCallback(hash) {
4
+ const params = new URLSearchParams(hash.replace(/^#/, ""));
5
+ const type = params.get("type");
6
+ if (type !== "invite" && type !== "recovery")
7
+ return null;
8
+ try {
9
+ // Decode only the payload inline. Never retain the access token.
10
+ const payload = params.get("access_token")?.split(".")[1];
11
+ params.delete("access_token");
12
+ if (!payload)
13
+ return null;
14
+ const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
15
+ const bytes = Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "=")), (c) => c.charCodeAt(0));
16
+ const decoded = JSON.parse(new TextDecoder().decode(bytes));
17
+ if (typeof decoded !== "object" || decoded === null || !("sub" in decoded))
18
+ return null;
19
+ return typeof decoded.sub === "string" && decoded.sub.length > 0 ? { type, sub: decoded.sub } : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ /** Called immediately before createClient can consume/clear the URL hash. */
26
+ export function capturePasswordCallback() {
27
+ if (typeof window === "undefined")
28
+ return;
29
+ callback = readPasswordCallback(window.location.hash);
30
+ }
31
+ export function getPasswordCallback() {
32
+ return callback;
33
+ }
34
+ export function clearPasswordCallback() {
35
+ callback = null;
36
+ }
37
+ export function matchingPasswordCallback(signal, session) {
38
+ return signal && session?.user.id === signal.sub ? signal.type : null;
39
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ResetWachtwoordPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/ResetWachtwoordPage.tsx"],"names":[],"mappings":"AASA,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,SAAoB,EAAE,EAAE,wBAAwB,2CAwFjG"}
1
+ {"version":3,"file":"ResetWachtwoordPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/ResetWachtwoordPage.tsx"],"names":[],"mappings":"AAUA,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,SAAoB,EAAE,EAAE,wBAAwB,2CA0IjG"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
2
+ import { useState, useEffect, useRef } from "react";
3
3
  import { useRouter } from "@tanstack/react-router";
4
4
  import { Button } from "../ui/button.js";
5
5
  import { Input } from "../ui/input.js";
@@ -7,23 +7,58 @@ import { Label } from "../ui/label.js";
7
7
  import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card.js";
8
8
  import { toast } from "sonner";
9
9
  import { supabase } from "../../supabase/client.js";
10
+ import { clearPasswordCallback, getPasswordCallback, matchingPasswordCallback, PASSWORD_CALLBACK_WAIT_MS } from "../../auth/password-callback.js";
10
11
  export function ResetWachtwoordPage({ redirectTo, loginPath = "/login" }) {
11
12
  const router = useRouter();
12
13
  const [password, setPassword] = useState("");
13
14
  const [confirmPassword, setConfirmPassword] = useState("");
14
15
  const [isLoading, setIsLoading] = useState(false);
15
- const [isReady, setIsReady] = useState(false);
16
+ const [callbackType, setCallbackType] = useState(null);
17
+ const [isWaiting, setIsWaiting] = useState(true);
18
+ const callback = useRef(null);
16
19
  useEffect(() => {
17
- const hash = window.location.hash;
18
- if (hash && hash.includes("type=recovery")) {
19
- setIsReady(true);
20
- }
21
- const { data: { subscription } } = supabase.auth.onAuthStateChange((event) => {
22
- if (event === "PASSWORD_RECOVERY") {
23
- setIsReady(true);
20
+ let active = true;
21
+ let accepted = false;
22
+ let timer;
23
+ // Accessing auth initializes the lazy client and captures the URL first.
24
+ const auth = supabase.auth;
25
+ callback.current = callback.current ?? getPasswordCallback();
26
+ const signal = callback.current;
27
+ const acceptSession = (session) => {
28
+ if (!active)
29
+ return;
30
+ const type = matchingPasswordCallback(signal, session);
31
+ setCallbackType(type);
32
+ if (type) {
33
+ accepted = true;
34
+ clearTimeout(timer);
35
+ clearPasswordCallback();
36
+ setIsWaiting(false);
37
+ }
38
+ else if (accepted) {
39
+ // A later sign-out or account switch invalidates this callback.
40
+ active = false;
41
+ callback.current = null;
42
+ setIsWaiting(false);
24
43
  }
44
+ };
45
+ const { data: { subscription } } = auth.onAuthStateChange((_event, session) => {
46
+ acceptSession(session);
25
47
  });
26
- return () => subscription.unsubscribe();
48
+ timer = setTimeout(() => {
49
+ active = false;
50
+ clearPasswordCallback();
51
+ callback.current = null;
52
+ setCallbackType(null);
53
+ setIsWaiting(false);
54
+ }, PASSWORD_CALLBACK_WAIT_MS);
55
+ void auth.getSession().then(({ data: { session }, error }) => {
56
+ if (!error)
57
+ acceptSession(session);
58
+ }).catch(() => {
59
+ // The bounded wait also covers session initialization/network failures.
60
+ });
61
+ return () => { active = false; clearTimeout(timer); clearPasswordCallback(); subscription.unsubscribe(); };
27
62
  }, []);
28
63
  const handleSubmit = async (e) => {
29
64
  e.preventDefault();
@@ -35,19 +70,34 @@ export function ResetWachtwoordPage({ redirectTo, loginPath = "/login" }) {
35
70
  toast.error("Wachtwoorden komen niet overeen");
36
71
  return;
37
72
  }
73
+ if (!callbackType)
74
+ return;
38
75
  setIsLoading(true);
76
+ const { data: { session }, error: sessionError } = await supabase.auth.getSession();
77
+ if (sessionError || !matchingPasswordCallback(callback.current, session)) {
78
+ clearPasswordCallback();
79
+ callback.current = null;
80
+ setCallbackType(null);
81
+ setIsLoading(false);
82
+ return;
83
+ }
39
84
  const { error } = await supabase.auth.updateUser({ password });
40
85
  setIsLoading(false);
41
86
  if (error) {
42
87
  toast.error("Fout bij het instellen van het nieuwe wachtwoord");
43
88
  }
44
89
  else {
90
+ clearPasswordCallback();
91
+ callback.current = null;
45
92
  toast.success("Wachtwoord succesvol gewijzigd!");
46
93
  await router.navigate({ href: redirectTo });
47
94
  }
48
95
  };
49
- if (!isReady) {
50
- return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { "data-empty-state": true, className: "text-2xl", children: "Ongeldige link" }), _jsx(CardDescription, { children: "Deze reset-link is ongeldig of verlopen. Vraag een nieuwe aan via de inlogpagina." })] }), _jsx(CardFooter, { className: "justify-center", children: _jsx(Button, { onClick: () => router.navigate({ href: loginPath }), children: "Naar inlogpagina" }) })] }) }));
96
+ if (isWaiting) {
97
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", role: "status", "aria-label": "Link controleren", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-b-2 border-primary motion-reduce:animate-none" }) }));
98
+ }
99
+ if (!callbackType) {
100
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { "data-empty-state": true, className: "text-2xl", children: "Ongeldige link" }), _jsx(CardDescription, { children: "Deze link is verlopen of al gebruikt. Vraag een nieuwe uitnodiging aan, of gebruik Wachtwoord vergeten op de inlogpagina." })] }), _jsx(CardFooter, { className: "justify-center", children: _jsx(Button, { onClick: () => router.navigate({ href: loginPath }), children: "Naar inlogpagina" }) })] }) }));
51
101
  }
52
- return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { className: "text-2xl", children: "Nieuw wachtwoord instellen" }), _jsx(CardDescription, { children: "Kies een nieuw wachtwoord voor je account" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "password", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] })] }), _jsx(CardFooter, { children: _jsx(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Opslaan..." : "Wachtwoord opslaan" }) })] })] }) }));
102
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { className: "text-2xl", children: callbackType === "invite" ? "Welkom!" : "Nieuw wachtwoord instellen" }), _jsx(CardDescription, { children: callbackType === "invite" ? "Kies een wachtwoord om je account te activeren." : "Kies een nieuw wachtwoord voor je account" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "password", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] })] }), _jsx(CardFooter, { children: _jsx(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Opslaan..." : "Wachtwoord opslaan" }) })] })] }) }));
53
103
  }
@@ -8,5 +8,5 @@ export { getRoleFlags } from "../auth/roles.js";
8
8
  export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
9
9
  export type { HandoverExchangeHandlerOptions } from "./handover-handler.js";
10
10
  export type { PermissionLevel } from "./permissions.server.js";
11
- export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
11
+ export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC"}
@@ -8,4 +8,4 @@ export { getRoleFlags } from "../auth/roles.js";
8
8
  export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
9
9
  // Shell-adres en Central-koppeling (alleen server; aangeroepen door de
10
10
  // door het dashboard gegenereerde globale request-middleware).
11
- export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
11
+ export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
@@ -13,12 +13,18 @@ export declare function getShellBaseUrl(): Promise<string | null>;
13
13
  */
14
14
  export declare function applyShellBaseUrl(): Promise<string | null>;
15
15
  /**
16
- * Hooguit eens per uur per isolate. Netwerkfout of ontbrekende RPC: niets
17
- * doen, waarschuwen, niet opnieuw tot het volgende uur.
16
+ * Na succes één uur wachten per isolate; na een fout vijf minuten.
18
17
  */
19
18
  export declare function ensureCentralLink(): Promise<void>;
20
- /** Voor de globale request-middleware van de shell. Gooit nooit. */
21
- export declare function runShellRuntime(): Promise<void>;
19
+ /** Synchroniseert de shell-publishable key; uur na succes, vijf minuten na fout. */
20
+ export declare function ensureCronKeys(): Promise<void>;
21
+ /** Alleen geregistreerde cronpaden; alle lees-/runtimefouten laten door. */
22
+ export declare function checkCronRequest(request: Request): Promise<Response | null>;
23
+ /** Zonder context hetzelfde gedrag als voorheen. Alleen bewuste cronweigering geeft 401. */
24
+ export declare function runShellRuntime(ctx?: {
25
+ request: Request;
26
+ pathname: string;
27
+ }): Promise<Response | null>;
22
28
  /** Alleen voor tests. */
23
29
  export declare function __resetShellRuntimeForTests(): void;
24
30
  //# sourceMappingURL=shell-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shell-runtime.d.ts","sourceRoot":"","sources":["../../src/core/server/shell-runtime.ts"],"names":[],"mappings":"AAwDA,iDAAiD;AACjD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASjE;AAmBD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqB9D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYhE;AAyDD;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAevD;AAED,oEAAoE;AACpE,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAMrD;AAED,yBAAyB;AACzB,wBAAgB,2BAA2B,SAM1C"}
1
+ {"version":3,"file":"shell-runtime.d.ts","sourceRoot":"","sources":["../../src/core/server/shell-runtime.ts"],"names":[],"mappings":"AAqEA,iDAAiD;AACjD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASjE;AAmBD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqB9D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYhE;AA+DD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBvD;AA4BD,oFAAoF;AACpF,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAWpD;AAyFD,4EAA4E;AAC5E,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAuBjF;AAED,4FAA4F;AAC5F,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU5G;AAED,yBAAyB;AACzB,wBAAgB,2BAA2B,SAW1C"}
@@ -14,14 +14,26 @@ const BASE_URL_TTL_MS = 60000;
14
14
  const LINK_INTERVAL_MS = 60 * 60000;
15
15
  const TIMEOUT_MS = 5000;
16
16
  const WARN_INTERVAL_MS = 5 * 60000;
17
+ const CRON_TTL_MS = 5 * 60000;
17
18
  let baseUrlCache = null;
18
19
  let baseUrlInflight = null;
19
- let lastLinkCheck = 0;
20
+ let nextLinkCheck = 0;
20
21
  let linkInflight = null;
22
+ let nextCronKeyCheck = 0;
23
+ let cronKeyInflight = null;
24
+ let cronCache = null;
25
+ let cronInflight = null;
26
+ let comparisonKey = null;
27
+ const DEFAULT_EXEMPT = [
28
+ "/api/public/print/queue-tick",
29
+ "/api/public/hooks/logistics-shipping-note-now",
30
+ "/api/public/cron/ping",
31
+ ];
21
32
  const lastWarn = new Map();
22
33
  function warn(key, message, detail) {
23
34
  const now = Date.now();
24
- if (now - (lastWarn.get(key) ?? 0) < WARN_INTERVAL_MS)
35
+ const last = lastWarn.get(key);
36
+ if (last !== undefined && now - last < WARN_INTERVAL_MS)
25
37
  return;
26
38
  lastWarn.set(key, now);
27
39
  console.warn(`[shell-runtime] ${message}`, detail ?? {});
@@ -70,11 +82,11 @@ async function readShellBaseUrl() {
70
82
  warn("config", "SUPABASE_URL of SB_SERVICE_ROLE_KEY ontbreekt; shell-adres onbekend");
71
83
  return null;
72
84
  }
73
- const res = await timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=shell_base_url&limit=1`, { method: "GET", headers: dbHeaders(cfg.key) });
85
+ const res = await timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=value&key=eq.shell_base_url&limit=1`, { method: "GET", headers: dbHeaders(cfg.key) });
74
86
  if (!res.ok)
75
87
  throw new Error(`database ${res.status}`);
76
88
  const rows = (await res.json());
77
- const value = normalizeShellBaseUrl(Array.isArray(rows) ? rows[0]?.shell_base_url : null);
89
+ const value = normalizeShellBaseUrl(Array.isArray(rows) ? rows[0]?.value : null);
78
90
  if (!value)
79
91
  warn("empty", "module_runtime_config.shell_base_url ontbreekt of is geen https-adres");
80
92
  return value;
@@ -122,8 +134,8 @@ export async function applyShellBaseUrl() {
122
134
  const current = process.env.PUBLIC_BASE_URL;
123
135
  if (current && normalizeShellBaseUrl(current) !== value) {
124
136
  warn("mismatch", "PUBLIC_BASE_URL wijkt af van de database; databasewaarde gebruikt", {
125
- env: current,
126
- database: value,
137
+ env: hostOnly(current),
138
+ database: hostOnly(value),
127
139
  });
128
140
  }
129
141
  process.env.PUBLIC_BASE_URL = value;
@@ -136,39 +148,47 @@ async function rpc(cfg, name, body) {
136
148
  body: JSON.stringify(body),
137
149
  });
138
150
  }
151
+ function hostOnly(value) {
152
+ try {
153
+ return new URL(value).host;
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
139
159
  async function linkOnce() {
140
160
  const cfg = dbConfig();
141
161
  const centralUrl = process.env.CENTRAL_AUTH_URL?.replace(/\/+$/, "");
142
162
  const secret = process.env.CENTRAL_AUTH_SECRET;
143
163
  if (!cfg || !centralUrl || !secret) {
144
164
  warn("link-config", "configuratie voor de Central-koppeling ontbreekt");
145
- return;
165
+ return false;
146
166
  }
147
167
  const statusRes = await rpc(cfg, "flowselections_central_status", {});
148
168
  if (!statusRes.ok) {
149
169
  warn("link-status", "flowselections_central_status niet beschikbaar", { status: statusRes.status });
150
- return;
170
+ return false;
151
171
  }
152
172
  const raw = await statusRes.json();
153
173
  const status = (Array.isArray(raw) ? raw[0] : raw);
154
- if (!status) {
174
+ if (!status || typeof status.token_aanwezig !== "boolean") {
155
175
  warn("link-status", "flowselections_central_status gaf geen resultaat");
156
- return;
176
+ return false;
157
177
  }
158
178
  if (status.token_aanwezig !== false && status.laatste_status !== "http_401")
159
- return;
179
+ return true;
160
180
  const tokenRes = await timedFetch(`${centralUrl}/api/shell-host/read-token`, {
161
181
  method: "POST",
162
182
  headers: { Authorization: `Bearer ${secret}`, Accept: "application/json" },
163
183
  });
164
184
  if (!tokenRes.ok) {
165
185
  warn("link-token", "leessleutel ophalen bij Central mislukt", { status: tokenRes.status });
166
- return;
186
+ return false;
167
187
  }
168
188
  const { token } = (await tokenRes.json());
169
189
  if (typeof token !== "string" || !token) {
170
190
  warn("link-token", "Central gaf geen bruikbare leessleutel");
171
- return;
191
+ return false;
172
192
  }
173
193
  const setRes = await rpc(cfg, "flowselections_set_central_link", {
174
194
  p_central_url: centralUrl,
@@ -176,21 +196,23 @@ async function linkOnce() {
176
196
  });
177
197
  if (!setRes.ok) {
178
198
  warn("link-set", "flowselections_set_central_link mislukt", { status: setRes.status });
199
+ return false;
179
200
  }
201
+ return true;
180
202
  }
181
203
  /**
182
- * Hooguit eens per uur per isolate. Netwerkfout of ontbrekende RPC: niets
183
- * doen, waarschuwen, niet opnieuw tot het volgende uur.
204
+ * Na succes één uur wachten per isolate; na een fout vijf minuten.
184
205
  */
185
206
  export async function ensureCentralLink() {
186
207
  const now = Date.now();
187
208
  if (linkInflight)
188
209
  return linkInflight;
189
- if (lastLinkCheck && now - lastLinkCheck < LINK_INTERVAL_MS)
210
+ if (now < nextLinkCheck)
190
211
  return;
191
- lastLinkCheck = now;
192
212
  linkInflight = linkOnce()
213
+ .then((ok) => { nextLinkCheck = Date.now() + (ok ? LINK_INTERVAL_MS : CRON_TTL_MS); })
193
214
  .catch((e) => {
215
+ nextLinkCheck = Date.now() + CRON_TTL_MS;
194
216
  warn("link-error", "Central-koppeling controleren mislukt", {
195
217
  error: e instanceof Error ? e.name : "onbekend",
196
218
  });
@@ -200,20 +222,189 @@ export async function ensureCentralLink() {
200
222
  });
201
223
  return linkInflight;
202
224
  }
203
- /** Voor de globale request-middleware van de shell. Gooit nooit. */
204
- export async function runShellRuntime() {
225
+ async function cronKeysOnce() {
226
+ const cfg = dbConfig();
227
+ const raw = process.env.SUPABASE_PUBLISHABLE_KEY ?? process.env.SUPABASE_ANON_KEY;
228
+ if (!cfg || !raw) {
229
+ warn("cron-key-config", "configuratie voor cronsleutels ontbreekt");
230
+ return false;
231
+ }
232
+ const key = raw.trim().replace(/^(["'])(.*)\1$/, "$2").trim();
233
+ if (!key) {
234
+ warn("cron-key-config", "publishable key ontbreekt");
235
+ return false;
236
+ }
237
+ const res = await rpc(cfg, "flowselections_set_anon_key", { p_key: key });
238
+ if (!res.ok) {
239
+ warn("cron-key-rpc", "flowselections_set_anon_key niet beschikbaar of mislukt", { status: res.status });
240
+ return false;
241
+ }
242
+ const result = await res.json();
243
+ if (result !== "ongewijzigd" && result !== "bijgewerkt") {
244
+ warn("cron-key-result", "flowselections_set_anon_key gaf geen geldig resultaat");
245
+ return false;
246
+ }
247
+ if (result === "bijgewerkt")
248
+ console.info("[shell-runtime] cronsleutel bijgewerkt");
249
+ return true;
250
+ }
251
+ /** Synchroniseert de shell-publishable key; uur na succes, vijf minuten na fout. */
252
+ export async function ensureCronKeys() {
253
+ if (cronKeyInflight)
254
+ return cronKeyInflight;
255
+ if (Date.now() < nextCronKeyCheck)
256
+ return;
257
+ cronKeyInflight = cronKeysOnce()
258
+ .then((ok) => { nextCronKeyCheck = Date.now() + (ok ? LINK_INTERVAL_MS : CRON_TTL_MS); })
259
+ .catch(() => {
260
+ nextCronKeyCheck = Date.now() + CRON_TTL_MS;
261
+ warn("cron-key-error", "cronsleutel synchroniseren mislukt");
262
+ })
263
+ .finally(() => { cronKeyInflight = null; });
264
+ return cronKeyInflight;
265
+ }
266
+ async function readCronConfig() {
267
+ const cfg = dbConfig();
268
+ if (!cfg) {
269
+ warn("cron-config", "databaseconfiguratie voor croncontrole ontbreekt");
270
+ return null;
271
+ }
272
+ const pathsRes = await rpc(cfg, "flowselections_cron_paths", {});
273
+ if (!pathsRes.ok) {
274
+ warn("cron-paths", "flowselections_cron_paths niet beschikbaar", { status: pathsRes.status });
275
+ return null;
276
+ }
277
+ const paths = await pathsRes.json();
278
+ if (!Array.isArray(paths) || !paths.every((p) => typeof p === "string" && p.startsWith("/"))) {
279
+ warn("cron-paths-result", "cronpaden hebben een ongeldig formaat");
280
+ return null;
281
+ }
282
+ if (!paths.length)
283
+ return { paths: [], secret: "", mode: "compat", exempt: DEFAULT_EXEMPT };
284
+ const [secretRes, configRes] = await Promise.all([
285
+ rpc(cfg, "flowselections_cron_secret", {}),
286
+ timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=key,value&key=in.(cron_auth_mode,cron_auth_exempt_paths)`, { method: "GET", headers: dbHeaders(cfg.key) }),
287
+ ]);
288
+ if (!secretRes.ok || !configRes.ok) {
289
+ warn("cron-read", "cronsleutel of cronconfiguratie niet beschikbaar", {
290
+ secretStatus: secretRes.status, configStatus: configRes.status,
291
+ });
292
+ return null;
293
+ }
294
+ const secret = await secretRes.json();
295
+ const rows = await configRes.json();
296
+ if (typeof secret !== "string" || !secret || !Array.isArray(rows)) {
297
+ warn("cron-result", "cronconfiguratie heeft een ongeldig formaat");
298
+ return null;
299
+ }
300
+ const config = new Map();
301
+ for (const row of rows) {
302
+ if (!row || typeof row !== "object" || typeof row.key !== "string" || typeof row.value !== "string") {
303
+ warn("cron-result", "cronconfiguratie heeft een ongeldig formaat");
304
+ return null;
305
+ }
306
+ config.set(row.key, row.value);
307
+ }
308
+ const mode = config.get("cron_auth_mode") ?? "compat";
309
+ if (mode !== "compat" && mode !== "afdwingen") {
310
+ warn("cron-mode", "onbekende cronmodus; controle overgeslagen");
311
+ return null;
312
+ }
313
+ const extra = (config.get("cron_auth_exempt_paths") ?? "").split(",").map((p) => p.trim()).filter(Boolean);
314
+ if (extra.some((p) => !p.startsWith("/"))) {
315
+ warn("cron-exempt", "ongeldig uitzonderingsprefix; controle overgeslagen");
316
+ return null;
317
+ }
318
+ return { paths, secret, mode, exempt: [...DEFAULT_EXEMPT, ...extra] };
319
+ }
320
+ async function getCronConfig(force = false, previous) {
321
+ if (cronInflight)
322
+ return cronInflight;
323
+ if (force && cronCache && Date.now() - cronCache.at < CRON_TTL_MS &&
324
+ (cronCache.value !== previous || cronCache.mismatchRefreshed))
325
+ return cronCache.value;
326
+ if (!force && cronCache && Date.now() - cronCache.at < CRON_TTL_MS)
327
+ return cronCache.value;
328
+ cronCache = null;
329
+ cronInflight = readCronConfig()
330
+ .catch(() => {
331
+ warn("cron-error", "croncontrole lezen mislukt");
332
+ return null;
333
+ })
334
+ .then((value) => {
335
+ cronCache = { value, at: Date.now(), mismatchRefreshed: force };
336
+ return value;
337
+ })
338
+ .finally(() => { cronInflight = null; });
339
+ return cronInflight;
340
+ }
341
+ // Native Web Crypto verifies a fixed-size HMAC tag in constant time. No Node imports.
342
+ async function sameSecret(expected, supplied) {
343
+ comparisonKey ?? (comparisonKey = crypto.subtle.generateKey({ name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]));
344
+ const key = await comparisonKey;
345
+ const encoder = new TextEncoder();
346
+ const tag = await crypto.subtle.sign("HMAC", key, encoder.encode(expected));
347
+ return crypto.subtle.verify("HMAC", key, tag, encoder.encode(supplied));
348
+ }
349
+ function needsCronCheck(config, pathname) {
350
+ return config.paths.includes(pathname) && !config.exempt.some((prefix) => pathname.startsWith(prefix));
351
+ }
352
+ /** Alleen geregistreerde cronpaden; alle lees-/runtimefouten laten door. */
353
+ export async function checkCronRequest(request) {
354
+ try {
355
+ const pathname = new URL(request.url).pathname;
356
+ // Permanently mixed-use routes never require a cron secret, even on DB errors.
357
+ if (DEFAULT_EXEMPT.some((prefix) => pathname.startsWith(prefix)))
358
+ return null;
359
+ let config = await getCronConfig();
360
+ if (!config || !needsCronCheck(config, pathname))
361
+ return null;
362
+ const supplied = request.headers.get("x-flowselections-cron-key");
363
+ if (supplied !== null && await sameSecret(config.secret, supplied))
364
+ return null;
365
+ if (supplied !== null) {
366
+ config = await getCronConfig(true, config);
367
+ if (!config || !needsCronCheck(config, pathname))
368
+ return null;
369
+ if (await sameSecret(config.secret, supplied))
370
+ return null;
371
+ }
372
+ if (config.mode === "afdwingen")
373
+ return new Response("Unauthorized", { status: 401 });
374
+ warn("cron-auth", "cronaanroep zonder geldige platformsleutel", {
375
+ path: pathname, reason: supplied === null ? "ontbreekt" : "wijkt af",
376
+ });
377
+ return null;
378
+ }
379
+ catch {
380
+ warn("cron-check", "croncontrole mislukt; verzoek doorgelaten");
381
+ return null;
382
+ }
383
+ }
384
+ /** Zonder context hetzelfde gedrag als voorheen. Alleen bewuste cronweigering geeft 401. */
385
+ export async function runShellRuntime(ctx) {
205
386
  try {
206
- await Promise.all([applyShellBaseUrl(), ensureCentralLink()]);
387
+ await Promise.allSettled([
388
+ applyShellBaseUrl(), ensureCentralLink(), ...(ctx ? [ensureCronKeys()] : []),
389
+ ]);
390
+ if (ctx)
391
+ return await checkCronRequest(ctx.request);
207
392
  }
208
393
  catch {
209
- // helpers vangen zelf; nooit een verzoek laten falen
394
+ // Geen onbedoelde weigering bij configuratie- of runtimefouten.
210
395
  }
396
+ return null;
211
397
  }
212
398
  /** Alleen voor tests. */
213
399
  export function __resetShellRuntimeForTests() {
214
400
  baseUrlCache = null;
215
401
  baseUrlInflight = null;
216
- lastLinkCheck = 0;
402
+ nextLinkCheck = 0;
217
403
  linkInflight = null;
404
+ nextCronKeyCheck = 0;
405
+ cronKeyInflight = null;
406
+ cronCache = null;
407
+ cronInflight = null;
408
+ comparisonKey = null;
218
409
  lastWarn.clear();
219
410
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAyB3C,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;8BAb8C,CAAC;0BAEnD,CAAC;;kBAAqD,CAAC;wBACrD,CAAC;sBAA0B,CAAC;;0BAM/C,CADC;;;;8BAGkB,CAAC;0BACL,CAAC;2BAA+B,CAAC;kBACzC,CAAC;wBAA6B,CAAA;sBAA0B,CAAC;qBAC3D,CAAC;0BACE,CAAC;uBAA2B,CAAC;;;;;;;;;;;;;0BAGwL,CAAC;;kBAA0D,CAAC;;0BAAuD,CAAC;;;0BAA4D,CAAC;gCAAoC,CAAC;kBAAsB,CAAC;qBAAyB,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;0BAAkqB,CAAC;;;0BAA0F,CAAC;kBAAsB,CAAC;yBAA6B,CAAC;0BAA+B,CAAC;0BAA8B,CAAC;;;0BAA4D,CAAC;yBAA6B,CAAC;2BAA+B,CAAC;0BAA8B,CAAC;kBAAsB,CAAC;yBAA6B,CAAC;0BAA+B,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;uBAA+iB,CAAC;oBAA+B,CAAC;;8BAAyE,CAAC;uBAAkC,CAAC;0BAAqC,CAAC;6BAAiC,CAAC;qBAAuB,CAAC;kBAA6B,CAAC;qBAAyB,CAAC;qBAAgC,CAAC;2BAAsC,CAAC;0BAAqC,CAAC;;;uBAAyD,CAAC;oBAA+B,CAAC;4BAAuC,CAAC;8BAAkC,CAAC;uBAAkC,CAAC;0BAAqC,CAAC;6BAAiC,CAAC;qBAAuB,CAAC;kBAA6B,CAAC;qBAAyB,CAAC;qBAAgC,CAAC;2BAAsC,CAAC;0BAAqC,CAAC;;;;;;;;;;;;;0BAAqT,CAAC;gCAAoC,CAAC;kBAAwB,CAAC;0BAA8B,CAAC;+BAAmC,CAAC;;;0BAA8D,CAAC;gCAAoC,CAAC;kBAAwB,CAAC;0BAA8B,CAAC;+BAAmC,CAAC;;;;;;;;;;;;;0BAAyR,CAAC;kBAAsB,CAAC;;0BAAsD,CAAC;0BAA8B,CAAC;;;0BAA4D,CAAC;kBAAsB,CAAC;oBAAwB,CAAC;0BAA8B,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;0BAAoV,CAAC;kBAAsB,CAAC;uBAA2B,CAAC;oBAA+B,CAAC;;0BAAwD,CAAC;;;;0BAAuF,CAAC;kBAAsB,CAAC;uBAA2B,CAAC;oBAA+B,CAAC;qBAA0B,CAAC;0BAA8B,CAAC;uBAA2B,CAAC;;;;;;;;;;;;;;;;qBAA2W,CAAC;0BAA8B,CAAC;oBAAwB,CAAC;kBAA6B,CAAC;;;0BAA8E,CAAC;0BAA8B,CAAC;;;qBAAuD,CAAC;0BAA8B,CAAC;oBAAwB,CAAC;kBAA6B,CAAC;oBAAwB,CAAC;oBAAwB,CAAC;0BAA8B,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;0BAA0S,CAAC;;kBAA0D,CAAC;;0BAAuD,CAAC;;;0BAA4D,CAAC;gCAAoC,CAAC;kBAAsB,CAAC;qBAAyB,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;mCAA6sB,CAAC;0BAA4B,CAAC;;;0BAA0F,CAAC;kBAAsB,CAAC;yBAA6B,CAAC;0BAA+B,CAAC;0BAA8B,CAAC;;;mCAAqE,CAAC;0BAA4B,CAAC;yBAA6B,CAAC;2BAA+B,CAAC;0BAA8B,CAAC;kBAAsB,CAAC;yBAA6B,CAAC;0BAA+B,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;uBAA0pB,CAAC;;0BAA8D,CAAC;6BAAiC,CAAC;kBAAoB,CAAC;yBAA6B,CAAC;iCAA4C,CAAC;;4BAA4D,CAAC;;4BAA2D,CAAC;8BAAkC,CAAC;wBAAmC,CAAC;0BAA8B,CAAC;oBAA+B,CAAC;0BAA8B,CAAC;sBAA0B,CAAC;;;uBAAgE,CAAC;qBAAgC,CAAC;0BAA8B,CAAC;6BAAiC,CAAC;kBAAoB,CAAC;yBAA6B,CAAC;iCAA4C,CAAC;wBAA4B,CAAC;4BAAgC,CAAC;uBAA2B,CAAC;4BAAgC,CAAC;8BAAkC,CAAC;wBAAmC,CAAC;0BAA8B,CAAC;oBAA+B,CAAC;0BAA8B,CAAC;sBAA0B,CAAC;;;;;;;;;;;;;;0BAAwV,CAAC;4BAAgC,CAAC;qBAAgC,CAAC;;wCAAyE,CAAC;0BAA4B,CAAC;;;0BAA4D,CAAC;4BAAgC,CAAC;qBAAgC,CAAC;kBAA6B,CAAC;wCAA4C,CAAC;0BAA4B,CAAC;;;;;;;;;;;;;;0BAAoV,CAAC;kBAAsB,CAAC;0BAA8B,CAAC;4BAAuC,CAAC;;wBAAiE,CAAC;;;0BAA4D,CAAC;kBAAsB,CAAC;0BAA8B,CAAC;4BAAuC,CAAC;iCAAqC,CAAC;wBAA4B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAAm+B,CAAC;;kBAAgE,CAAC;2BAA+B,CAAC;qBAAgC,CAAC;;sBAAiE,CAAC;2BAA+B,CAAC;6BAAwC,CAAC;0BAA8B,CAAC;;;0BAA4D,CAAC;sCAA0C,CAAC;kBAAsB,CAAC;2BAA+B,CAAC;qBAAgC,CAAC;4BAAuC,CAAC;sBAA0B,CAAC;2BAA+B,CAAC;6BAAwC,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAA69B,CAAC;0BAAqC,CAAC;kBAAsB,CAAC;;qBAAiD,CAAC;wBAAmC,CAAC;qBAAgC,CAAC;wBAAmC,CAAC;;oBAA8D,CAAC;0BAA8B,CAAC;;;8BAAgE,CAAC;0BAAqC,CAAC;kBAAsB,CAAC;oBAAwB,CAAC;qBAAyB,CAAC;wBAAmC,CAAC;qBAAgC,CAAC;wBAAmC,CAAC;2BAAsC,CAAC;oBAAwB,CAAC;0BAA8B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;uBAA8xB,CAAC;+BAA0C,CAAC;8BAAuC,CAAC;0BAAqC,CAAC;oCAAwC,CAAC;qBAAyB,CAAC;kBAA6B,CAAC;kCAAsC,CAAC;;qBAAwD,CAAC;qBAAgC,CAAC;0BAAqC,CAAC;;;uBAAyD,CAAC;+BAA0C,CAAC;8BAAuC,CAAC;0BAAqC,CAAC;oCAAwC,CAAC;qBAAyB,CAAC;kBAA6B,CAAC;kCAAsC,CAAC;oBAA+B,CAAC;qBAAyB,CAAC;qBAAgC,CAAC;0BAAqC,CAAC;;;;;;;;sBAAiK,kBAAmB,SAAS;;;;0BAA2F,CAAC;kBAAsB,CAAC;sBAAyB,kBAAmB,SAAS;;;;0BAA2F,CAAC;kBAAsB,CAAC;oBAAwB,CAAC,EAAC,kBAAmB,SAAS;uBAA+B,CAAC;;;;;;;;;;;;;;;;;;;EAA1igB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  // This file is automatically generated. Do not edit it directly.
2
2
  import { createClient } from '@supabase/supabase-js';
3
+ import { capturePasswordCallback } from '../auth/password-callback.js';
3
4
  function createSupabaseClient() {
4
5
  const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL || process.env.SUPABASE_URL;
5
6
  const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY || process.env.SUPABASE_PUBLISHABLE_KEY;
6
7
  if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) {
7
8
  throw new Error('Missing Supabase environment variables. Ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY (or SUPABASE_URL/SUPABASE_PUBLISHABLE_KEY) are set.');
8
9
  }
10
+ capturePasswordCallback();
9
11
  return createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
10
12
  auth: {
11
13
  storage: typeof window !== 'undefined' ? window.localStorage : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/core",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",