@flowselections/twinfield-authenticatie-module-15-06-2026 1.3.6 → 1.3.8

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 (42) hide show
  1. package/dist-lib/components/settings/TwinfieldAuthSettingsCard.d.ts.map +1 -1
  2. package/dist-lib/components/settings/TwinfieldAuthSettingsCard.js +34 -5
  3. package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -1
  4. package/dist-lib/integrations/supabase/auth-attacher.js +9 -1
  5. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1856 -78
  6. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  7. package/dist-lib/integrations/supabase/client.d.ts +1856 -78
  8. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  9. package/dist-lib/integrations/supabase/client.server.d.ts +1856 -78
  10. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  11. package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts +1856 -78
  12. package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/types.d.ts +1880 -75
  14. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  15. package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts +8970 -80
  16. package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts.map +1 -1
  17. package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts +1856 -78
  18. package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/accounting/twinfield/cron-auth.server.d.ts +4 -0
  20. package/dist-lib/lib/accounting/twinfield/cron-auth.server.d.ts.map +1 -0
  21. package/dist-lib/lib/accounting/twinfield/cron-auth.server.js +56 -0
  22. package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts +5382 -48
  23. package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts.map +1 -1
  24. package/dist-lib/lib/central/central-accounts.server.d.ts.map +1 -1
  25. package/dist-lib/lib/central/central-accounts.server.js +2 -1
  26. package/dist-lib/lib/central/central-token.server.d.ts +12 -0
  27. package/dist-lib/lib/central/central-token.server.d.ts.map +1 -1
  28. package/dist-lib/lib/central/central-token.server.js +72 -14
  29. package/dist-lib/lib/twinfield-adapter.functions.d.ts +3760 -204
  30. package/dist-lib/lib/twinfield-adapter.functions.d.ts.map +1 -1
  31. package/dist-lib/lib/twinfield-sync.functions.d.ts +8970 -80
  32. package/dist-lib/lib/twinfield-sync.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/twinfield-sync.server.d.ts +24 -0
  34. package/dist-lib/lib/twinfield-sync.server.d.ts.map +1 -1
  35. package/dist-lib/lib/twinfield-sync.server.js +639 -154
  36. package/dist-lib/lib/twinfield-token.server.d.ts +20 -1
  37. package/dist-lib/lib/twinfield-token.server.d.ts.map +1 -1
  38. package/dist-lib/lib/twinfield-token.server.js +104 -29
  39. package/dist-lib/lib/twinfield.functions.d.ts +26916 -240
  40. package/dist-lib/lib/twinfield.functions.d.ts.map +1 -1
  41. package/dist-lib/lib/twinfield.functions.js +79 -17
  42. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"AAmTA,wBAAgB,yBAAyB,CAAC,EACxC,eAAsB,GACvB,GAAE;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,+BA6UL"}
1
+ {"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"AA0WA,wBAAgB,yBAAyB,CAAC,EACxC,eAAsB,GACvB,GAAE;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,+BA6UL"}
@@ -50,7 +50,13 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
50
50
  const saveOffice = useServerFn(setTwinfieldConnectionOffice);
51
51
  const disconnect = useServerFn(disconnectTwinfieldConnection);
52
52
  const trigger = useServerFn(triggerTwinfieldSync);
53
+ const startOAuthForCard = useServerFn(startTwinfieldOAuth);
53
54
  const [offices, setOffices] = useState([]);
55
+ const [officesStatus, setOfficesStatus] = useState("empty");
56
+ const [officesMessage, setOfficesMessage] = useState(null);
57
+ const [needsReauth, setNeedsReauth] = useState(false);
58
+ const [reauthorizing, setReauthorizing] = useState(false);
59
+ const [officesReload, setOfficesReload] = useState(0);
54
60
  const [loadingOffices, setLoadingOffices] = useState(false);
55
61
  const [savingOffice, setSavingOffice] = useState(false);
56
62
  const [optimisticOffice, setOptimisticOffice] = useState(null);
@@ -65,10 +71,19 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
65
71
  if (!cancelled) {
66
72
  const list = Array.isArray(res?.offices) ? res.offices : [];
67
73
  setOffices(list);
74
+ setOfficesStatus(res?.status ??
75
+ (list.length > 0 ? "ok" : "empty"));
76
+ setOfficesMessage(res?.message ?? null);
77
+ setNeedsReauth(Boolean(res?.needsReauth));
68
78
  }
69
79
  }
70
80
  catch {
71
- /* silent */
81
+ if (!cancelled) {
82
+ setOffices([]);
83
+ setOfficesStatus("error");
84
+ setOfficesMessage("Administraties konden niet opgehaald worden.");
85
+ setNeedsReauth(false);
86
+ }
72
87
  }
73
88
  finally {
74
89
  if (!cancelled)
@@ -78,7 +93,20 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
78
93
  return () => {
79
94
  cancelled = true;
80
95
  };
81
- }, [conn.id, loadOffices]);
96
+ }, [conn.id, loadOffices, officesReload]);
97
+ const handleReauthorize = async () => {
98
+ setReauthorizing(true);
99
+ try {
100
+ const res = await startOAuthForCard({ data: undefined });
101
+ if (!res?.url)
102
+ throw new Error("Kon geen koppelingsverzoek starten.");
103
+ window.location.href = res.url;
104
+ }
105
+ catch (e) {
106
+ toast.error(e?.message ?? "Autoriseren niet gelukt.");
107
+ setReauthorizing(false);
108
+ }
109
+ };
82
110
  // Wanneer server-waarde overeenkomt met optimistische keuze: opruimen.
83
111
  useEffect(() => {
84
112
  if (optimisticOffice && conn.office_code === optimisticOffice.code) {
@@ -147,12 +175,13 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
147
175
  const effectiveOfficeCode = conn.office_code ?? optimisticOffice?.code ?? null;
148
176
  const effectiveOfficeName = conn.company_name ?? optimisticOffice?.name ?? null;
149
177
  const displayName = effectiveOfficeName ?? effectiveOfficeCode ?? conn.company_code ?? "Nog geen administratie gekozen";
150
- return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: conn.connected ? "default" : "secondary", className: "font-normal", children: conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] })] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
178
+ return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: needsReauth ? "destructive" : conn.connected ? "default" : "secondary", className: "font-normal", children: needsReauth ? "Autorisatie vereist" : conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] })] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
151
179
  const selected = offices.find((o) => o.code === effectiveOfficeCode);
152
180
  if (!selected)
153
181
  return null;
154
182
  return (_jsxs("span", { className: "flex items-baseline gap-2 truncate", children: [_jsx("span", { className: "truncate", children: selected.name ?? selected.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: selected.code })] }));
155
- })() }) }), _jsx(SelectContent, { children: offices.map((o) => (_jsx(SelectItem, { value: o.code, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: o.name ?? o.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: o.code })] }) }, o.code))) })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children: loadingOffices ? "Administraties ophalen…" : "Geen administraties gevonden." }))] }), effectiveOfficeCode && (_jsxs("div", { className: "flex flex-wrap gap-2 pt-1", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("customers"), disabled: busy === "customers", children: busy === "customers" ? "Debiteuren…" : "Debiteuren bijwerken" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("invoices"), disabled: busy === "invoices", children: busy === "invoices" ? "Facturen…" : "Facturen bijwerken" })] }))] }));
183
+ })() }) }), _jsx(SelectContent, { children: offices.map((o) => (_jsx(SelectItem, { value: o.code, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: o.name ?? o.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: o.code })] }) }, o.code))) })] })) : loadingOffices ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Administraties ophalen\u2026" })) : officesStatus === "error" ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs text-destructive", children: officesMessage ?? "Administraties konden niet opgehaald worden." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => setOfficesReload((n) => n + 1), children: "Opnieuw proberen" }), needsReauth && (_jsxs(Button, { size: "sm", onClick: handleReauthorize, disabled: reauthorizing, children: [_jsx(Link2, { className: "h-4 w-4 mr-1.5" }), reauthorizing ? "Bezig…" : "Opnieuw autoriseren"] }))] })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children: officesMessage ??
184
+ "Dit Twinfield-account heeft geen administraties. Koppel een account met toegang tot een administratie." }))] }), effectiveOfficeCode && (_jsxs("div", { className: "flex flex-wrap gap-2 pt-1", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("customers"), disabled: busy === "customers", children: busy === "customers" ? "Debiteuren…" : "Debiteuren bijwerken" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("invoices"), disabled: busy === "invoices", children: busy === "invoices" ? "Facturen…" : "Facturen bijwerken" })] }))] }));
156
185
  }
157
186
  export function TwinfieldAuthSettingsCard({ defaultExpanded = true, } = {}) {
158
187
  const startOAuth = useServerFn(startTwinfieldOAuth);
@@ -193,7 +222,7 @@ export function TwinfieldAuthSettingsCard({ defaultExpanded = true, } = {}) {
193
222
  }
194
223
  }
195
224
  const [connRes, stateRes, queueRes] = await Promise.all([
196
- listConnections(),
225
+ listConnections().catch(() => null),
197
226
  getSyncState().catch(() => null),
198
227
  fetchPushQueue().catch(() => null),
199
228
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,mHAS9B,CAAA"}
1
+ {"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,mHAgB9B,CAAA"}
@@ -5,7 +5,15 @@
5
5
  import { createMiddleware } from '@tanstack/react-start';
6
6
  export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client(async ({ next }) => {
7
7
  const { supabase } = await import('./client');
8
- const { data } = await supabase.auth.getSession();
8
+ let { data } = await supabase.auth.getSession();
9
+ // Proactief verversen: een token dat binnen 60s verloopt geeft server-side
10
+ // een 401 Response, wat als "Error: [object Response]" op het scherm belandt.
11
+ const expiresAt = data.session?.expires_at ?? 0;
12
+ if (data.session && expiresAt * 1000 - Date.now() < 60000) {
13
+ const refreshed = await supabase.auth.refreshSession().catch(() => null);
14
+ if (refreshed?.data?.session)
15
+ data = refreshed.data;
16
+ }
9
17
  const token = data.session?.access_token;
10
18
  return next({
11
19
  headers: token ? { Authorization: `Bearer ${token}` } : {},