@olmocms/front 0.1.5 → 0.1.7

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.
@@ -25,7 +25,6 @@ __export(action_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(action_exports);
27
27
  var import_headers = require("next/headers");
28
- var import_navigation = require("next/navigation");
29
28
 
30
29
  // src/auth/stage.ts
31
30
  var COOKIE_NAME = "olmo_stage_auth";
@@ -98,7 +97,7 @@ async function stageLoginAction(_prev, formData) {
98
97
  path: "/",
99
98
  expires: new Date(exp)
100
99
  });
101
- (0, import_navigation.redirect)(safeRedirectTarget(formData.get("from")));
100
+ return { redirectTo: safeRedirectTarget(formData.get("from")) };
102
101
  }
103
102
  // Annotate the CommonJS export names for ESM import in node:
104
103
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { redirect } from 'next/navigation';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n redirect(safeRedirectTarget(formData.get('from')));\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,qBAAwB;AACxB,wBAAyB;;;ACAzB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADxFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,UAAM,wBAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAED,kCAAS,mBAAmB,SAAS,IAAI,MAAM,CAAC,CAAC;AACnD;","names":[]}
1
+ {"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n // Return the target instead of calling redirect() here. The client-side\n // <StageLogin> reads this in a useEffect and calls router.replace, which\n // guarantees the Set-Cookie has been committed to the browser jar before\n // the next GET fires. This eliminates the cookie-commit vs redirect race\n // that surfaces on Vercel's edge runtime + CDN.\n return { redirectTo: safeRedirectTarget(formData.get('from')) };\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,qBAAwB;;;ACCxB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADtFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,UAAM,wBAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAOD,SAAO,EAAE,YAAY,mBAAmB,SAAS,IAAI,MAAM,CAAC,EAAE;AAChE;","names":[]}
@@ -1,5 +1,6 @@
1
1
  type StageLoginState = {
2
2
  error?: 'missing_config' | 'invalid_credentials' | null;
3
+ redirectTo?: string | null;
3
4
  };
4
5
  declare function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState>;
5
6
 
@@ -1,5 +1,6 @@
1
1
  type StageLoginState = {
2
2
  error?: 'missing_config' | 'invalid_credentials' | null;
3
+ redirectTo?: string | null;
3
4
  };
4
5
  declare function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState>;
5
6
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  // src/auth/action.ts
4
4
  import { cookies } from "next/headers";
5
- import { redirect } from "next/navigation";
6
5
 
7
6
  // src/auth/stage.ts
8
7
  var COOKIE_NAME = "olmo_stage_auth";
@@ -75,7 +74,7 @@ async function stageLoginAction(_prev, formData) {
75
74
  path: "/",
76
75
  expires: new Date(exp)
77
76
  });
78
- redirect(safeRedirectTarget(formData.get("from")));
77
+ return { redirectTo: safeRedirectTarget(formData.get("from")) };
79
78
  }
80
79
  export {
81
80
  stageLoginAction
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { redirect } from 'next/navigation';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = { error?: 'missing_config' | 'invalid_credentials' | null };\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n redirect(safeRedirectTarget(formData.get('from')));\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;AAEA,SAAS,eAAe;AACxB,SAAS,gBAAgB;;;ACAzB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADxFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAED,WAAS,mBAAmB,SAAS,IAAI,MAAM,CAAC,CAAC;AACnD;","names":[]}
1
+ {"version":3,"sources":["../../src/auth/action.ts","../../src/auth/stage.ts"],"sourcesContent":["// 'use server' is injected by the tsup banner — keep this file directive-free\n// so the bundled output has exactly one directive at the top.\nimport { cookies } from 'next/headers';\nimport { getStageCookieName, readStageEnv, signSession, verifyCredentials } from './stage.js';\n\nexport type StageLoginState = {\n error?: 'missing_config' | 'invalid_credentials' | null;\n redirectTo?: string | null;\n};\n\nfunction safeRedirectTarget(raw: unknown): string {\n if (typeof raw !== 'string' || raw.length === 0) return '/';\n if (!raw.startsWith('/') || raw.startsWith('//')) return '/';\n return raw;\n}\n\nexport async function stageLoginAction(_prev: StageLoginState, formData: FormData): Promise<StageLoginState> {\n const env = readStageEnv();\n if (!env) return { error: 'missing_config' };\n\n const username = formData.get('username');\n const password = formData.get('password');\n if (!verifyCredentials(env, username, password)) {\n return { error: 'invalid_credentials' };\n }\n\n const exp = Date.now() + env.sessionDays * 24 * 60 * 60 * 1000;\n const token = await signSession({ u: env.username, exp }, env.secret);\n\n const store = await cookies();\n store.set(getStageCookieName(), token, {\n httpOnly: true,\n // secure must be false on HTTP dev (next dev on localhost) so the browser stores\n // the cookie. In production builds (next start / Vercel) this becomes true.\n secure: process.env.NODE_ENV === 'production',\n sameSite: 'lax',\n path: '/',\n expires: new Date(exp),\n });\n\n // Return the target instead of calling redirect() here. The client-side\n // <StageLogin> reads this in a useEffect and calls router.replace, which\n // guarantees the Set-Cookie has been committed to the browser jar before\n // the next GET fires. This eliminates the cookie-commit vs redirect race\n // that surfaces on Vercel's edge runtime + CDN.\n return { redirectTo: safeRedirectTarget(formData.get('from')) };\n}\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":";;;AAEA,SAAS,eAAe;;;ACCxB,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAWtB,SAAS,eAAgC;AAC9C,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,YAAY,CAAC,YAAY,CAAC,OAAQ,QAAO;AAC9C,QAAM,OAAO,OAAO,QAAQ,IAAI,uBAAuB;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO;AAAA,EAC1D;AACF;AAEO,SAAS,qBAA6B;AAC3C,SAAO;AACT;AAEA,SAAS,YAAY,OAAyC;AAC5D,QAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AACtE,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,QAAO,OAAO,aAAa,IAAI,CAAC,CAAC;AACtE,SAAO,KAAK,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC5E;AAWA,eAAe,UAAU,QAAoC;AAC3D,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,MAAM;AAAA,IAC/B,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,YAAY,SAAuB,QAAiC;AACxF,QAAM,OAAO,YAAY,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAC1E,QAAM,MAAM,MAAM,UAAU,MAAM;AAClC,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAChF,SAAO,GAAG,IAAI,IAAI,YAAY,GAAG,CAAC;AACpC;AA6BO,SAAS,kBAAkB,KAAe,UAAmB,UAA4B;AAC9F,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU,QAAO;AACzE,SAAO,aAAa,IAAI,YAAY,aAAa,IAAI;AACvD;;;ADtFA,SAAS,mBAAmB,KAAsB;AAChD,MAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAAG,QAAO;AACxD,MAAI,CAAC,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AACzD,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAwB,UAA8C;AAC3G,QAAM,MAAM,aAAa;AACzB,MAAI,CAAC,IAAK,QAAO,EAAE,OAAO,iBAAiB;AAE3C,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,QAAM,WAAW,SAAS,IAAI,UAAU;AACxC,MAAI,CAAC,kBAAkB,KAAK,UAAU,QAAQ,GAAG;AAC/C,WAAO,EAAE,OAAO,sBAAsB;AAAA,EACxC;AAEA,QAAM,MAAM,KAAK,IAAI,IAAI,IAAI,cAAc,KAAK,KAAK,KAAK;AAC1D,QAAM,QAAQ,MAAM,YAAY,EAAE,GAAG,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM;AAEpE,QAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAM,IAAI,mBAAmB,GAAG,OAAO;AAAA,IACrC,UAAU;AAAA;AAAA;AAAA,IAGV,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,IAAI,KAAK,GAAG;AAAA,EACvB,CAAC;AAOD,SAAO,EAAE,YAAY,mBAAmB,SAAS,IAAI,MAAM,CAAC,EAAE;AAChE;","names":[]}
@@ -0,0 +1,121 @@
1
+ // src/client/index.ts
2
+ var API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;
3
+ var API_TOKEN = process.env.OLMO_TOKEN;
4
+ var BASE_HEADERS = {
5
+ Accept: "application/json",
6
+ "Content-Type": "application/json",
7
+ "front-token": API_TOKEN
8
+ };
9
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
10
+ var MAX_RETRIES = 4;
11
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+ function backoffDelay(attempt, retryAfterHeader) {
13
+ const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : NaN;
14
+ if (Number.isFinite(retryAfter) && retryAfter > 0) {
15
+ return Math.min(retryAfter * 1e3, 1e4);
16
+ }
17
+ return Math.min(500 * 2 ** attempt, 8e3) + Math.floor(Math.random() * 250);
18
+ }
19
+ async function fetchWithRetry(url, init, label) {
20
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
21
+ let response;
22
+ try {
23
+ response = await fetch(url, init);
24
+ } catch (error) {
25
+ if (attempt === MAX_RETRIES) {
26
+ console.error(`[olmo:${label}] Network error after ${attempt} retries \u2014 ${url}:`, error);
27
+ return void 0;
28
+ }
29
+ await sleep(backoffDelay(attempt, null));
30
+ continue;
31
+ }
32
+ if (response.ok || !RETRYABLE_STATUS.has(response.status) || attempt === MAX_RETRIES) {
33
+ return response;
34
+ }
35
+ const delay = backoffDelay(attempt, response.headers.get("retry-after"));
36
+ console.warn(
37
+ `[olmo:${label}] ${response.status} ${response.statusText} \u2014 retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms \u2014 ${url}`
38
+ );
39
+ await sleep(delay);
40
+ }
41
+ return void 0;
42
+ }
43
+ async function getting(lang, path, model = "") {
44
+ const isPreview = path.includes("?olmopreview");
45
+ const response = await fetchWithRetry(
46
+ `${API_BASE_URL}${path}`,
47
+ {
48
+ method: "GET",
49
+ headers: BASE_HEADERS,
50
+ cache: isPreview ? "no-cache" : "force-cache",
51
+ next: { tags: ["olmo", lang, path, model] }
52
+ },
53
+ "get"
54
+ );
55
+ if (!response || !response.ok) {
56
+ if (response) {
57
+ console.error(`[olmo:get] ${response.status} ${response.statusText} \u2014 ${path}`);
58
+ }
59
+ return void 0;
60
+ }
61
+ return await response.json();
62
+ }
63
+ async function posting(lang = "it", path, body) {
64
+ const response = await fetchWithRetry(
65
+ `${API_BASE_URL}/${lang}/${path}`,
66
+ {
67
+ method: "POST",
68
+ headers: BASE_HEADERS,
69
+ body: JSON.stringify(body),
70
+ cache: "force-cache",
71
+ next: { tags: ["olmo", `/${lang}/allmodel/${path}`] }
72
+ },
73
+ "post"
74
+ );
75
+ if (!response || !response.ok) {
76
+ if (response) {
77
+ console.error(`[olmo:post] ${response.status} ${response.statusText} \u2014 /${lang}/${path}`);
78
+ }
79
+ return void 0;
80
+ }
81
+ const data = await response.json();
82
+ if (data.errors) {
83
+ console.error(`[olmo:post] API errors \u2014 /${lang}/${path}:`, data.errors);
84
+ return void 0;
85
+ }
86
+ return data.response;
87
+ }
88
+ async function filter(lang = "it", path, body) {
89
+ try {
90
+ const response = await fetchWithRetry(
91
+ `${API_BASE_URL}/${lang}/filters/${path}`,
92
+ {
93
+ method: "POST",
94
+ headers: BASE_HEADERS,
95
+ body: JSON.stringify(body)
96
+ },
97
+ "filter"
98
+ );
99
+ if (!response || !response.ok) {
100
+ const status = response ? `${response.status} ${response.statusText}` : "no response";
101
+ console.error(`[olmo:filter] ${status} \u2014 /${lang}/filters/${path}`);
102
+ throw new Error(`Olmo API error: ${status}`);
103
+ }
104
+ const data = await response.json();
105
+ if (data.errors) {
106
+ console.error(`[olmo:filter] API errors \u2014 /${lang}/filters/${path}:`, data.errors);
107
+ throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);
108
+ }
109
+ return data;
110
+ } catch (error) {
111
+ console.error(`[olmo:filter] Error \u2014 /${lang}/filters/${path}:`, error);
112
+ throw error;
113
+ }
114
+ }
115
+
116
+ export {
117
+ getting,
118
+ posting,
119
+ filter
120
+ };
121
+ //# sourceMappingURL=chunk-4XDE23ZJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/index.ts"],"sourcesContent":["// Next.js extends RequestInit with a `next` property for cache tags / revalidation.\ntype NextRequestInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\nconst API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;\nconst API_TOKEN = process.env.OLMO_TOKEN as string;\n\nconst BASE_HEADERS: HeadersInit = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'front-token': API_TOKEN,\n};\n\n// During static generation (SSG/ISR) the framework fans out many requests in\n// parallel, which can trip the API's rate limiter (429) or hit a transient 5xx.\n// A single failure used to abort the whole `next build`. We instead retry such\n// responses with exponential backoff + jitter (honouring `Retry-After`), so a\n// momentary hiccup self-heals instead of failing the deploy. Non-retryable\n// statuses (e.g. 404) return immediately.\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\nconst MAX_RETRIES = 4;\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nfunction backoffDelay(attempt: number, retryAfterHeader: string | null): number {\n const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : NaN;\n if (Number.isFinite(retryAfter) && retryAfter > 0) {\n return Math.min(retryAfter * 1000, 10000);\n }\n // 0.5s, 1s, 2s, 4s … capped at 8s, plus jitter to de-sync parallel workers.\n return Math.min(500 * 2 ** attempt, 8000) + Math.floor(Math.random() * 250);\n}\n\nasync function fetchWithRetry(\n url: string,\n init: NextRequestInit,\n label: string,\n): Promise<Response | undefined> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n let response: Response | undefined;\n try {\n response = await fetch(url, init);\n } catch (error) {\n if (attempt === MAX_RETRIES) {\n console.error(`[olmo:${label}] Network error after ${attempt} retries — ${url}:`, error);\n return undefined;\n }\n await sleep(backoffDelay(attempt, null));\n continue;\n }\n\n if (response.ok || !RETRYABLE_STATUS.has(response.status) || attempt === MAX_RETRIES) {\n return response;\n }\n\n const delay = backoffDelay(attempt, response.headers.get('retry-after'));\n console.warn(\n `[olmo:${label}] ${response.status} ${response.statusText} — retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms — ${url}`,\n );\n await sleep(delay);\n }\n\n return undefined;\n}\n\nexport async function getting<T = unknown>(\n lang: string,\n path: string,\n model = '',\n): Promise<T | undefined> {\n const isPreview = path.includes('?olmopreview');\n\n const response = await fetchWithRetry(\n `${API_BASE_URL}${path}`,\n {\n method: 'GET',\n headers: BASE_HEADERS,\n cache: isPreview ? 'no-cache' : 'force-cache',\n next: { tags: ['olmo', lang, path, model] },\n } as NextRequestInit,\n 'get',\n );\n\n if (!response || !response.ok) {\n if (response) {\n console.error(`[olmo:get] ${response.status} ${response.statusText} — ${path}`);\n }\n return undefined;\n }\n\n return (await response.json()) as T;\n}\n\nexport async function posting<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T | undefined> {\n const response = await fetchWithRetry(\n `${API_BASE_URL}/${lang}/${path}`,\n {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n cache: 'force-cache',\n next: { tags: ['olmo', `/${lang}/allmodel/${path}`] },\n } as NextRequestInit,\n 'post',\n );\n\n // Degrade gracefully instead of throwing: a failed POST must not abort a\n // static build. Callers should default the result (e.g. `?? []`).\n if (!response || !response.ok) {\n if (response) {\n console.error(`[olmo:post] ${response.status} ${response.statusText} — /${lang}/${path}`);\n }\n return undefined;\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:post] API errors — /${lang}/${path}:`, data.errors);\n return undefined;\n }\n\n return data.response as T;\n}\n\nexport async function filter<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetchWithRetry(\n `${API_BASE_URL}/${lang}/filters/${path}`,\n {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n } as NextRequestInit,\n 'filter',\n );\n\n if (!response || !response.ok) {\n const status = response ? `${response.status} ${response.statusText}` : 'no response';\n console.error(`[olmo:filter] ${status} — /${lang}/filters/${path}`);\n throw new Error(`Olmo API error: ${status}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:filter] API errors — /${lang}/filters/${path}:`, data.errors);\n throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data as T;\n } catch (error) {\n console.error(`[olmo:filter] Error — /${lang}/filters/${path}:`, error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -30,7 +30,7 @@ var redirectmiddleware = (next) => {
30
30
  if (request.headers.has("rsc")) {
31
31
  return next(request, _next);
32
32
  }
33
- if (!process.env.NEXT_PUBLIC_OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {
33
+ if (!process.env.OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {
34
34
  return next(request, _next);
35
35
  }
36
36
  const pathNameWithTrailingSlash = request.nextUrl.pathname;
@@ -38,7 +38,7 @@ var redirectmiddleware = (next) => {
38
38
  method: "GET",
39
39
  cache: "force-cache",
40
40
  next: { tags: ["olmo", "redirect"] },
41
- headers: { "front-token": process.env.NEXT_PUBLIC_OLMO_TOKEN }
41
+ headers: { "front-token": process.env.OLMO_TOKEN }
42
42
  });
43
43
  const data = await response.json();
44
44
  const redirects = data.map((e) => ({
@@ -155,6 +155,7 @@ var stagegatemiddleware = (next) => {
155
155
  }
156
156
  const url = request.nextUrl.clone();
157
157
  url.pathname = LOGIN_PATH;
158
+ url.search = `?from=${encodeURIComponent(pathname + search)}`;
158
159
  return withNoindex(NextResponse3.redirect(url, { status: 307 }));
159
160
  };
160
161
  };
@@ -165,4 +166,4 @@ export {
165
166
  redirectmiddleware,
166
167
  stagegatemiddleware
167
168
  };
168
- //# sourceMappingURL=chunk-RAI2AUWN.js.map
169
+ //# sourceMappingURL=chunk-LXZ3LAUY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/chain.ts","../src/middleware/headermiddleware.ts","../src/middleware/redirectmiddleware.ts","../src/middleware/stagegatemiddleware.ts","../src/auth/stage.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { NextMiddleware } from 'next/server';\n\nexport type MiddlewareFactory = (middleware: NextMiddleware) => NextMiddleware;\n\nexport function chain(functions: MiddlewareFactory[] = [], index = 0): NextMiddleware {\n const current = functions[index];\n if (current) {\n const next = chain(functions, index + 1);\n return current(next);\n }\n return () => NextResponse.next();\n}\n","import { NextRequest } from 'next/server';\nimport type { NextFetchEvent } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\nexport const headermiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n const url = new URL(request.url);\n const params = new URLSearchParams(url.search);\n\n const headers = new Headers(request.headers);\n headers.set('x-current-path', request.nextUrl.pathname);\n headers.set('x-server', 'true');\n headers.set('olmo-preview', params.has('olmopreview').toString());\n\n return next(new NextRequest(request.url, { headers }), _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport type { MiddlewareFactory } from './chain.js';\n\ntype NextFetchRequestInit = RequestInit & { next?: { tags?: string[]; revalidate?: number | false } };\n\nexport const redirectmiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (request.headers.has('rsc')) {\n return next(request, _next);\n }\n\n if (!process.env.OLMO_TOKEN || !process.env.NEXT_PUBLIC_API_URL || !process.env.NEXT_PUBLIC_BASE_URL) {\n return next(request, _next);\n }\n\n const pathNameWithTrailingSlash = request.nextUrl.pathname;\n\n const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/all/redirect`, {\n method: 'GET',\n cache: 'force-cache',\n next: { tags: ['olmo', 'redirect'] },\n headers: { 'front-token': process.env.OLMO_TOKEN },\n } as NextFetchRequestInit);\n\n const data = await response.json();\n\n const redirects = data.map((e: any) => ({\n source: e.source,\n destination: e.destination,\n permanent: e.permanent === 'true',\n }));\n\n if (redirects.length > 0) {\n const redirect = redirects.find((item: any) => item.source === pathNameWithTrailingSlash);\n\n if (!redirect) {\n return next(request, _next);\n }\n\n const newUrl = new URL(redirect.destination, process.env.NEXT_PUBLIC_BASE_URL).toString();\n return NextResponse.redirect(newUrl, { status: redirect.permanent ? 308 : 307 });\n }\n\n return next(request, _next);\n };\n};\n","import { NextResponse } from 'next/server';\nimport type { NextFetchEvent, NextRequest } from 'next/server';\nimport { getStageCookieName, readStageEnv, verifySession } from '../auth/stage.js';\nimport type { MiddlewareFactory } from './chain.js';\n\nconst LOGIN_PATH = '/olmo-stage-login/';\nconst NOINDEX = 'noindex, nofollow, noarchive';\n\nfunction withNoindex(res: NextResponse): NextResponse {\n res.headers.set('X-Robots-Tag', NOINDEX);\n return res;\n}\n\nexport const stagegatemiddleware: MiddlewareFactory = (next) => {\n return async (request: NextRequest, _next: NextFetchEvent) => {\n if (process.env.NEXT_PUBLIC_ENV !== 'stage') {\n return next(request, _next);\n }\n\n const { pathname, search } = request.nextUrl;\n\n if (pathname === LOGIN_PATH) {\n return withNoindex(NextResponse.next());\n }\n\n const env = readStageEnv();\n if (!env) {\n // Misconfigured stage — deny everything except the login page itself.\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = '';\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n }\n\n const token = request.cookies.get(getStageCookieName())?.value;\n const session = await verifySession(token, env.secret);\n if (session) {\n const res = await next(request, _next);\n return withNoindex(res instanceof NextResponse ? res : NextResponse.next());\n }\n\n const url = request.nextUrl.clone();\n url.pathname = LOGIN_PATH;\n url.search = `?from=${encodeURIComponent(pathname + search)}`;\n return withNoindex(NextResponse.redirect(url, { status: 307 }));\n };\n};\n","// Internal helpers for the stage-environment auth gate.\n// Edge-runtime safe (uses Web Crypto, no Node APIs).\n\nconst COOKIE_NAME = 'olmo_stage_auth';\nconst DEFAULT_SESSION_DAYS = 7;\n\nexport type StagePayload = { u: string; exp: number };\n\nexport type StageEnv = {\n username: string;\n password: string;\n secret: string;\n sessionDays: number;\n};\n\nexport function readStageEnv(): StageEnv | null {\n const username = process.env.OLMO_STAGE_USERNAME;\n const password = process.env.OLMO_STAGE_PASSWORD;\n const secret = process.env.OLMO_STAGE_SECRET;\n if (!username || !password || !secret) return null;\n const days = Number(process.env.OLMO_STAGE_SESSION_DAYS);\n return {\n username,\n password,\n secret,\n sessionDays: Number.isFinite(days) && days > 0 ? days : DEFAULT_SESSION_DAYS,\n };\n}\n\nexport function getStageCookieName(): string {\n return COOKIE_NAME;\n}\n\nfunction toBase64Url(bytes: ArrayBuffer | Uint8Array): string {\n const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n let str = '';\n for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction fromBase64Url(input: string): Uint8Array {\n const pad = input.length % 4 === 0 ? '' : '='.repeat(4 - (input.length % 4));\n const b64 = input.replace(/-/g, '+').replace(/_/g, '/') + pad;\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\nexport async function signSession(payload: StagePayload, secret: string): Promise<string> {\n const body = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));\n const key = await importKey(secret);\n const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));\n return `${body}.${toBase64Url(sig)}`;\n}\n\nexport async function verifySession(token: string | undefined, secret: string): Promise<StagePayload | null> {\n if (!token || typeof token !== 'string' || token.indexOf('.') === -1) return null;\n const [body, sig] = token.split('.');\n if (!body || !sig) return null;\n let key: CryptoKey;\n try {\n key = await importKey(secret);\n } catch {\n return null;\n }\n let valid = false;\n try {\n valid = await crypto.subtle.verify('HMAC', key, fromBase64Url(sig) as BufferSource, new TextEncoder().encode(body));\n } catch {\n return null;\n }\n if (!valid) return null;\n let payload: StagePayload;\n try {\n payload = JSON.parse(new TextDecoder().decode(fromBase64Url(body)));\n } catch {\n return null;\n }\n if (!payload || typeof payload.exp !== 'number' || payload.exp < Date.now()) return null;\n return payload;\n}\n\nexport function verifyCredentials(env: StageEnv, username: unknown, password: unknown): boolean {\n if (typeof username !== 'string' || typeof password !== 'string') return false;\n return username === env.username && password === env.password;\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","url"]}
@@ -26,64 +26,106 @@ __export(client_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(client_exports);
28
28
  var API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;
29
- var API_TOKEN = process.env.NEXT_PUBLIC_OLMO_TOKEN;
29
+ var API_TOKEN = process.env.OLMO_TOKEN;
30
30
  var BASE_HEADERS = {
31
31
  Accept: "application/json",
32
32
  "Content-Type": "application/json",
33
33
  "front-token": API_TOKEN
34
34
  };
35
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
36
+ var MAX_RETRIES = 4;
37
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
38
+ function backoffDelay(attempt, retryAfterHeader) {
39
+ const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : NaN;
40
+ if (Number.isFinite(retryAfter) && retryAfter > 0) {
41
+ return Math.min(retryAfter * 1e3, 1e4);
42
+ }
43
+ return Math.min(500 * 2 ** attempt, 8e3) + Math.floor(Math.random() * 250);
44
+ }
45
+ async function fetchWithRetry(url, init, label) {
46
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
47
+ let response;
48
+ try {
49
+ response = await fetch(url, init);
50
+ } catch (error) {
51
+ if (attempt === MAX_RETRIES) {
52
+ console.error(`[olmo:${label}] Network error after ${attempt} retries \u2014 ${url}:`, error);
53
+ return void 0;
54
+ }
55
+ await sleep(backoffDelay(attempt, null));
56
+ continue;
57
+ }
58
+ if (response.ok || !RETRYABLE_STATUS.has(response.status) || attempt === MAX_RETRIES) {
59
+ return response;
60
+ }
61
+ const delay = backoffDelay(attempt, response.headers.get("retry-after"));
62
+ console.warn(
63
+ `[olmo:${label}] ${response.status} ${response.statusText} \u2014 retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms \u2014 ${url}`
64
+ );
65
+ await sleep(delay);
66
+ }
67
+ return void 0;
68
+ }
35
69
  async function getting(lang, path, model = "") {
36
70
  const isPreview = path.includes("?olmopreview");
37
- try {
38
- const response = await fetch(`${API_BASE_URL}${path}`, {
71
+ const response = await fetchWithRetry(
72
+ `${API_BASE_URL}${path}`,
73
+ {
39
74
  method: "GET",
40
75
  headers: BASE_HEADERS,
41
76
  cache: isPreview ? "no-cache" : "force-cache",
42
77
  next: { tags: ["olmo", lang, path, model] }
43
- });
44
- if (!response.ok) {
78
+ },
79
+ "get"
80
+ );
81
+ if (!response || !response.ok) {
82
+ if (response) {
45
83
  console.error(`[olmo:get] ${response.status} ${response.statusText} \u2014 ${path}`);
46
- return void 0;
47
84
  }
48
- return await response.json();
49
- } catch (error) {
50
- console.error(`[olmo:get] Network error \u2014 ${path}:`, error);
51
85
  return void 0;
52
86
  }
87
+ return await response.json();
53
88
  }
54
89
  async function posting(lang = "it", path, body) {
55
- try {
56
- const response = await fetch(`${API_BASE_URL}/${lang}/${path}`, {
90
+ const response = await fetchWithRetry(
91
+ `${API_BASE_URL}/${lang}/${path}`,
92
+ {
57
93
  method: "POST",
58
94
  headers: BASE_HEADERS,
59
95
  body: JSON.stringify(body),
60
96
  cache: "force-cache",
61
97
  next: { tags: ["olmo", `/${lang}/allmodel/${path}`] }
62
- });
63
- if (!response.ok) {
98
+ },
99
+ "post"
100
+ );
101
+ if (!response || !response.ok) {
102
+ if (response) {
64
103
  console.error(`[olmo:post] ${response.status} ${response.statusText} \u2014 /${lang}/${path}`);
65
- throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);
66
104
  }
67
- const data = await response.json();
68
- if (data.errors) {
69
- throw new Error(`Olmo API returned errors for /${lang}/${path}: ${JSON.stringify(data.errors)}`);
70
- }
71
- return data.response;
72
- } catch (error) {
73
- console.error(`[olmo:post] Error \u2014 /${lang}/${path}:`, error);
74
- throw error;
105
+ return void 0;
75
106
  }
107
+ const data = await response.json();
108
+ if (data.errors) {
109
+ console.error(`[olmo:post] API errors \u2014 /${lang}/${path}:`, data.errors);
110
+ return void 0;
111
+ }
112
+ return data.response;
76
113
  }
77
114
  async function filter(lang = "it", path, body) {
78
115
  try {
79
- const response = await fetch(`${API_BASE_URL}/${lang}/filters/${path}`, {
80
- method: "POST",
81
- headers: BASE_HEADERS,
82
- body: JSON.stringify(body)
83
- });
84
- if (!response.ok) {
85
- console.error(`[olmo:filter] ${response.status} ${response.statusText} \u2014 /${lang}/filters/${path}`);
86
- throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);
116
+ const response = await fetchWithRetry(
117
+ `${API_BASE_URL}/${lang}/filters/${path}`,
118
+ {
119
+ method: "POST",
120
+ headers: BASE_HEADERS,
121
+ body: JSON.stringify(body)
122
+ },
123
+ "filter"
124
+ );
125
+ if (!response || !response.ok) {
126
+ const status = response ? `${response.status} ${response.statusText}` : "no response";
127
+ console.error(`[olmo:filter] ${status} \u2014 /${lang}/filters/${path}`);
128
+ throw new Error(`Olmo API error: ${status}`);
87
129
  }
88
130
  const data = await response.json();
89
131
  if (data.errors) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["// Next.js extends RequestInit with a `next` property for cache tags / revalidation.\ntype NextRequestInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\nconst API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;\nconst API_TOKEN = process.env.NEXT_PUBLIC_OLMO_TOKEN as string;\n\nconst BASE_HEADERS: HeadersInit = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'front-token': API_TOKEN,\n};\n\nexport async function getting<T = unknown>(\n lang: string,\n path: string,\n model = '',\n): Promise<T | undefined> {\n const isPreview = path.includes('?olmopreview');\n\n try {\n const response = await fetch(`${API_BASE_URL}${path}`, {\n method: 'GET',\n headers: BASE_HEADERS,\n cache: isPreview ? 'no-cache' : 'force-cache',\n next: { tags: ['olmo', lang, path, model] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:get] ${response.status} ${response.statusText} — ${path}`);\n return undefined;\n }\n\n return (await response.json()) as T;\n } catch (error) {\n console.error(`[olmo:get] Network error — ${path}:`, error);\n return undefined;\n }\n}\n\nexport async function posting<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n cache: 'force-cache',\n next: { tags: ['olmo', `/${lang}/allmodel/${path}`] },\n } as NextRequestInit);\n\n if (!response.ok) {\n console.error(`[olmo:post] ${response.status} ${response.statusText} — /${lang}/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n throw new Error(`Olmo API returned errors for /${lang}/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data.response as T;\n } catch (error) {\n console.error(`[olmo:post] Error — /${lang}/${path}:`, error);\n throw error;\n }\n}\n\nexport async function filter<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetch(`${API_BASE_URL}/${lang}/filters/${path}`, {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n console.error(`[olmo:filter] ${response.status} ${response.statusText} — /${lang}/filters/${path}`);\n throw new Error(`Olmo API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:filter] API errors — /${lang}/filters/${path}:`, data.errors);\n throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data as T;\n } catch (error) {\n console.error(`[olmo:filter] Error — /${lang}/filters/${path}:`, error);\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,IAAM,eAAe,QAAQ,IAAI;AACjC,IAAM,YAAY,QAAQ,IAAI;AAE9B,IAAM,eAA4B;AAAA,EAChC,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,eAAe;AACjB;AAEA,eAAsB,QACpB,MACA,MACA,QAAQ,IACgB;AACxB,QAAM,YAAY,KAAK,SAAS,cAAc;AAE9C,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,YAAY,GAAG,IAAI,IAAI;AAAA,MACrD,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,OAAO,YAAY,aAAa;AAAA,MAChC,MAAM,EAAE,MAAM,CAAC,QAAQ,MAAM,MAAM,KAAK,EAAE;AAAA,IAC5C,CAAoB;AAEpB,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,cAAc,SAAS,MAAM,IAAI,SAAS,UAAU,WAAM,IAAI,EAAE;AAC9E,aAAO;AAAA,IACT;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,YAAQ,MAAM,mCAA8B,IAAI,KAAK,KAAK;AAC1D,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,QACpB,OAAO,MACP,MACA,MACY;AACZ,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,YAAY,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,OAAO;AAAA,MACP,MAAM,EAAE,MAAM,CAAC,QAAQ,IAAI,IAAI,aAAa,IAAI,EAAE,EAAE;AAAA,IACtD,CAAoB;AAEpB,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,eAAe,SAAS,MAAM,IAAI,SAAS,UAAU,YAAO,IAAI,IAAI,IAAI,EAAE;AACxF,YAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,IAC7E;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,iCAAiC,IAAI,IAAI,IAAI,KAAK,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AAAA,IACjG;AAEA,WAAO,KAAK;AAAA,EACd,SAAS,OAAO;AACd,YAAQ,MAAM,6BAAwB,IAAI,IAAI,IAAI,KAAK,KAAK;AAC5D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,OACpB,OAAO,MACP,MACA,MACY;AACZ,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,YAAY,IAAI,IAAI,YAAY,IAAI,IAAI;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,iBAAiB,SAAS,MAAM,IAAI,SAAS,UAAU,YAAO,IAAI,YAAY,IAAI,EAAE;AAClG,YAAM,IAAI,MAAM,mBAAmB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,IAC7E;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,KAAK,QAAQ;AACf,cAAQ,MAAM,oCAA+B,IAAI,YAAY,IAAI,KAAK,KAAK,MAAM;AACjF,YAAM,IAAI,MAAM,iCAAiC,IAAI,YAAY,IAAI,KAAK,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AAAA,IACzG;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,+BAA0B,IAAI,YAAY,IAAI,KAAK,KAAK;AACtE,UAAM;AAAA,EACR;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["// Next.js extends RequestInit with a `next` property for cache tags / revalidation.\ntype NextRequestInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\nconst API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;\nconst API_TOKEN = process.env.OLMO_TOKEN as string;\n\nconst BASE_HEADERS: HeadersInit = {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'front-token': API_TOKEN,\n};\n\n// During static generation (SSG/ISR) the framework fans out many requests in\n// parallel, which can trip the API's rate limiter (429) or hit a transient 5xx.\n// A single failure used to abort the whole `next build`. We instead retry such\n// responses with exponential backoff + jitter (honouring `Retry-After`), so a\n// momentary hiccup self-heals instead of failing the deploy. Non-retryable\n// statuses (e.g. 404) return immediately.\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\nconst MAX_RETRIES = 4;\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nfunction backoffDelay(attempt: number, retryAfterHeader: string | null): number {\n const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : NaN;\n if (Number.isFinite(retryAfter) && retryAfter > 0) {\n return Math.min(retryAfter * 1000, 10000);\n }\n // 0.5s, 1s, 2s, 4s … capped at 8s, plus jitter to de-sync parallel workers.\n return Math.min(500 * 2 ** attempt, 8000) + Math.floor(Math.random() * 250);\n}\n\nasync function fetchWithRetry(\n url: string,\n init: NextRequestInit,\n label: string,\n): Promise<Response | undefined> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n let response: Response | undefined;\n try {\n response = await fetch(url, init);\n } catch (error) {\n if (attempt === MAX_RETRIES) {\n console.error(`[olmo:${label}] Network error after ${attempt} retries — ${url}:`, error);\n return undefined;\n }\n await sleep(backoffDelay(attempt, null));\n continue;\n }\n\n if (response.ok || !RETRYABLE_STATUS.has(response.status) || attempt === MAX_RETRIES) {\n return response;\n }\n\n const delay = backoffDelay(attempt, response.headers.get('retry-after'));\n console.warn(\n `[olmo:${label}] ${response.status} ${response.statusText} — retry ${attempt + 1}/${MAX_RETRIES} in ${delay}ms — ${url}`,\n );\n await sleep(delay);\n }\n\n return undefined;\n}\n\nexport async function getting<T = unknown>(\n lang: string,\n path: string,\n model = '',\n): Promise<T | undefined> {\n const isPreview = path.includes('?olmopreview');\n\n const response = await fetchWithRetry(\n `${API_BASE_URL}${path}`,\n {\n method: 'GET',\n headers: BASE_HEADERS,\n cache: isPreview ? 'no-cache' : 'force-cache',\n next: { tags: ['olmo', lang, path, model] },\n } as NextRequestInit,\n 'get',\n );\n\n if (!response || !response.ok) {\n if (response) {\n console.error(`[olmo:get] ${response.status} ${response.statusText} — ${path}`);\n }\n return undefined;\n }\n\n return (await response.json()) as T;\n}\n\nexport async function posting<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T | undefined> {\n const response = await fetchWithRetry(\n `${API_BASE_URL}/${lang}/${path}`,\n {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n cache: 'force-cache',\n next: { tags: ['olmo', `/${lang}/allmodel/${path}`] },\n } as NextRequestInit,\n 'post',\n );\n\n // Degrade gracefully instead of throwing: a failed POST must not abort a\n // static build. Callers should default the result (e.g. `?? []`).\n if (!response || !response.ok) {\n if (response) {\n console.error(`[olmo:post] ${response.status} ${response.statusText} — /${lang}/${path}`);\n }\n return undefined;\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:post] API errors /${lang}/${path}:`, data.errors);\n return undefined;\n }\n\n return data.response as T;\n}\n\nexport async function filter<T = unknown>(\n lang = 'it',\n path: string,\n body: object,\n): Promise<T> {\n try {\n const response = await fetchWithRetry(\n `${API_BASE_URL}/${lang}/filters/${path}`,\n {\n method: 'POST',\n headers: BASE_HEADERS,\n body: JSON.stringify(body),\n } as NextRequestInit,\n 'filter',\n );\n\n if (!response || !response.ok) {\n const status = response ? `${response.status} ${response.statusText}` : 'no response';\n console.error(`[olmo:filter] ${status} — /${lang}/filters/${path}`);\n throw new Error(`Olmo API error: ${status}`);\n }\n\n const data = await response.json();\n\n if (data.errors) {\n console.error(`[olmo:filter] API errors — /${lang}/filters/${path}:`, data.errors);\n throw new Error(`Olmo API returned errors for /${lang}/filters/${path}: ${JSON.stringify(data.errors)}`);\n }\n\n return data as T;\n } catch (error) {\n console.error(`[olmo:filter] Error — /${lang}/filters/${path}:`, error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  declare function getting<T = unknown>(lang: string, path: string, model?: string): Promise<T | undefined>;
2
- declare function posting<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T>;
2
+ declare function posting<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T | undefined>;
3
3
  declare function filter<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T>;
4
4
 
5
5
  export { filter, getting, posting };
@@ -1,5 +1,5 @@
1
1
  declare function getting<T = unknown>(lang: string, path: string, model?: string): Promise<T | undefined>;
2
- declare function posting<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T>;
2
+ declare function posting<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T | undefined>;
3
3
  declare function filter<T = unknown>(lang: string | undefined, path: string, body: object): Promise<T>;
4
4
 
5
5
  export { filter, getting, posting };
@@ -2,7 +2,7 @@ import {
2
2
  filter,
3
3
  getting,
4
4
  posting
5
- } from "../chunk-DMHYDI2A.js";
5
+ } from "../chunk-4XDE23ZJ.js";
6
6
  export {
7
7
  filter,
8
8
  getting,
@@ -130,8 +130,12 @@ function StageLogin({
130
130
  missingConfigMessage = "Stage authentication is not configured."
131
131
  }) {
132
132
  const params = (0, import_navigation.useSearchParams)();
133
+ const router = (0, import_navigation.useRouter)();
133
134
  const from = params.get("from") ?? "/";
134
135
  const [state, formAction, pending] = (0, import_react4.useActionState)(action, {});
136
+ (0, import_react4.useEffect)(() => {
137
+ if (state.redirectTo) router.replace(state.redirectTo);
138
+ }, [state.redirectTo, router]);
135
139
  const errorMessage = state.error === "invalid_credentials" ? invalidCredentialsMessage : state.error === "missing_config" ? missingConfigMessage : null;
136
140
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-full flex-col justify-center px-6 py-12 lg:px-8", children: [
137
141
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "sm:mx-auto sm:w-full sm:max-w-[384px]", children: [
@@ -142,6 +146,7 @@ function StageLogin({
142
146
  ] })
143
147
  ] }),
144
148
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-10 sm:mx-auto sm:w-full sm:max-w-[384px]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { action: formAction, method: "POST", className: "space-y-6", children: [
149
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "hidden", name: "from", value: from }),
145
150
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
146
151
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { htmlFor: "username", className: "block text-sm/6 font-medium text-gray-900", children: usernameLabel }),
147
152
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(