@palbase/web 2.0.0 → 3.0.0
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 +11 -9
- package/dist/{analytics-facade-ovP0pF2P.d.cts → analytics-facade-BOxQ8IP2.d.cts} +11 -10
- package/dist/{analytics-facade-DMtGDGfd.d.ts → analytics-facade-yfMJ69vw.d.ts} +11 -10
- package/dist/{chunk-V6VTX65P.js → chunk-3HVTEZ7N.js} +80 -58
- package/dist/chunk-3HVTEZ7N.js.map +1 -0
- package/dist/{chunk-VJXFABBW.js → chunk-6VGKMNXO.js} +11 -11
- package/dist/chunk-6VGKMNXO.js.map +1 -0
- package/dist/chunk-S7NAY3MW.js +24 -0
- package/dist/chunk-S7NAY3MW.js.map +1 -0
- package/dist/gen/cli.cjs +33 -8
- package/dist/gen/cli.cjs.map +1 -1
- package/dist/gen/cli.js +29 -9
- package/dist/gen/cli.js.map +1 -1
- package/dist/index.cjs +53 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/internal.cjs +83 -54
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +4 -4
- package/dist/internal.d.ts +4 -4
- package/dist/internal.js +2 -2
- package/dist/next/client.cjs +100 -68
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.d.cts +1 -1
- package/dist/next/client.d.ts +1 -1
- package/dist/next/client.js +12 -10
- package/dist/next/client.js.map +1 -1
- package/dist/next/index.cjs +103 -69
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +11 -11
- package/dist/next/index.d.ts +11 -11
- package/dist/next/index.js +14 -13
- package/dist/next/index.js.map +1 -1
- package/dist/{pb-DDM_mCQB.d.cts → pb-B3h3NIPE.d.cts} +1 -1
- package/dist/{pb-CvpcQero.d.ts → pb-QRISxPd1.d.ts} +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/package.json +4 -4
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-V6VTX65P.js.map +0 -1
- package/dist/chunk-VJXFABBW.js.map +0 -1
|
@@ -6,11 +6,11 @@ var SESSION_COOKIE_ATTRS = {
|
|
|
6
6
|
secure: true,
|
|
7
7
|
maxAge: 2592e3
|
|
8
8
|
};
|
|
9
|
-
function sessionCookieName(
|
|
10
|
-
return `palbe-session-${
|
|
9
|
+
function sessionCookieName(environmentRef) {
|
|
10
|
+
return `palbe-session-${environmentRef}`;
|
|
11
11
|
}
|
|
12
|
-
function encodeSessionCookies(
|
|
13
|
-
const name = sessionCookieName(
|
|
12
|
+
function encodeSessionCookies(environmentRef, session) {
|
|
13
|
+
const name = sessionCookieName(environmentRef);
|
|
14
14
|
const value = encodeURIComponent(
|
|
15
15
|
JSON.stringify({ a: session.accessToken, r: session.refreshToken, e: session.expiresAt })
|
|
16
16
|
);
|
|
@@ -29,15 +29,15 @@ function encodeSessionCookies(endpointRef, session) {
|
|
|
29
29
|
}
|
|
30
30
|
return { set: chunks, clear: [`${name}.${chunks.length}`] };
|
|
31
31
|
}
|
|
32
|
-
function encodeSessionCookiesDecoded(
|
|
33
|
-
const { set, clear } = encodeSessionCookies(
|
|
32
|
+
function encodeSessionCookiesDecoded(environmentRef, session) {
|
|
33
|
+
const { set, clear } = encodeSessionCookies(environmentRef, session);
|
|
34
34
|
return {
|
|
35
35
|
set: set.map(({ name, value }) => ({ name, value: decodeURIComponent(value) })),
|
|
36
36
|
clear
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
function decodeSessionCookies(get,
|
|
40
|
-
const base = sessionCookieName(
|
|
39
|
+
function decodeSessionCookies(get, environmentRef) {
|
|
40
|
+
const base = sessionCookieName(environmentRef);
|
|
41
41
|
let encoded = get(base);
|
|
42
42
|
if (encoded === void 0) {
|
|
43
43
|
let joined = "";
|
|
@@ -71,8 +71,8 @@ function parseStoredSession(json) {
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
function clearedSessionCookieNames(
|
|
75
|
-
const base = sessionCookieName(
|
|
74
|
+
function clearedSessionCookieNames(environmentRef, present) {
|
|
75
|
+
const base = sessionCookieName(environmentRef);
|
|
76
76
|
const names = [];
|
|
77
77
|
if (present(base)) names.push(base);
|
|
78
78
|
for (let i = 0; ; i++) {
|
|
@@ -91,4 +91,4 @@ export {
|
|
|
91
91
|
decodeSessionCookies,
|
|
92
92
|
clearedSessionCookieNames
|
|
93
93
|
};
|
|
94
|
-
//# sourceMappingURL=chunk-
|
|
94
|
+
//# sourceMappingURL=chunk-6VGKMNXO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/next/cookie-codec.ts"],"sourcesContent":["/**\n * Session-cookie codec shared by EVERY palbe/next surface (browser storage\n * adapter, pbServer cookie adapter, middleware, auth callback). Pure\n * functions — no 'next' imports, no palbe runtime imports — so both sides\n * provably read and write the SAME bytes.\n *\n * Format (P3 design contract):\n * - name: `palbe-session-<environmentRef>`; overflow chunk names append `.0`,\n * `.1`, … (cookie names otherwise avoid dots — tooling friendliness).\n * - value: `encodeURIComponent(JSON.stringify({ a, r, e }))` with a=access\n * token, r=refresh token, e=expiry (ms epoch).\n * - one cookie when the encoded value fits MAX_COOKIE_VALUE chars, else\n * chunks ONLY (the base name must not coexist with chunks — callers clear\n * stale names via clearedSessionCookieNames before writing, then honor the\n * encode result's `clear` overflow guard after writing).\n */\n\nexport { environmentRefFromApiKey } from '../api-key.js';\n\nexport interface StoredSession {\n accessToken: string;\n refreshToken: string;\n /** ms epoch */\n expiresAt: number;\n}\n\n/** Browsers cap a cookie at ~4096 bytes incl. name + attributes; 3500 leaves headroom. */\nconst MAX_COOKIE_VALUE = 3500;\n\n/**\n * The P3 cookie contract attributes, shared by every server-side writer\n * (pbServer adapter, middleware, callback); the browser writer mirrors them\n * in document.cookie string form (next/client.ts). NOT HttpOnly by design —\n * the browser SDK must read/write the session (Supabase-paradigm tradeoff);\n * Max-Age = 30d = the refresh-token TTL.\n */\nexport const SESSION_COOKIE_ATTRS = {\n path: '/',\n sameSite: 'lax',\n secure: true,\n maxAge: 2_592_000,\n} as const;\n\nexport function sessionCookieName(environmentRef: string): string {\n return `palbe-session-${environmentRef}`;\n}\n\nexport interface SessionCookieWrite {\n /** Cookies to SET, in order. */\n set: Array<{ name: string; value: string }>;\n /**\n * Names to DELETE alongside the set: the one-past-the-end OVERFLOW GUARD\n * (base write → `.0`; chunks `.0..n` → `.(n+1)`). Decode stops at the\n * first missing chunk, so deleting exactly one-past-the-end guarantees a\n * stale orphan chunk (left behind a gap, where presence-probing cleanup\n * cannot see it) can never concatenate into the freshly written run.\n * Writers MUST honor both lists.\n */\n clear: string[];\n}\n\nexport function encodeSessionCookies(\n environmentRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const name = sessionCookieName(environmentRef);\n const value = encodeURIComponent(\n JSON.stringify({ a: session.accessToken, r: session.refreshToken, e: session.expiresAt }),\n );\n if (value.length <= MAX_COOKIE_VALUE) {\n return { set: [{ name, value }], clear: [`${name}.0`] };\n }\n const chunks: Array<{ name: string; value: string }> = [];\n // Chunk WITHOUT splitting a %XX escape (every '%' in the encoded value\n // starts one — encodeURIComponent never emits a bare '%'). Next's cookie\n // layer percent-DECODES values on read and silently DROPS a cookie whose\n // value is an invalid escape sequence, so a split escape would lose that\n // chunk server-side; independent decodability also lets\n // encodeSessionCookiesDecoded mirror this run byte-exactly.\n for (let start = 0; start < value.length; ) {\n let end = Math.min(start + MAX_COOKIE_VALUE, value.length);\n if (end < value.length) {\n if (value[end - 1] === '%') end -= 1;\n else if (value[end - 2] === '%') end -= 2;\n }\n chunks.push({ name: `${name}.${chunks.length}`, value: value.slice(start, end) });\n start = end;\n }\n return { set: chunks, clear: [`${name}.${chunks.length}`] };\n}\n\n/**\n * encodeSessionCookies for percent-ENCODING cookie stores. Next's request\n * and response cookie APIs apply encodeURIComponent to values when\n * serializing (and decodeURIComponent on read) — handing them the codec's\n * already-encoded values would DOUBLE-encode the wire bytes and break the\n * browser-side reader. This variant returns the same cookie run with\n * DECODED values: `encodeURIComponent(value)` reproduces the\n * encodeSessionCookies bytes exactly (chunk boundaries never split an\n * escape), so an encoding store writes byte-identical cookies to a raw\n * writer like document.cookie.\n */\nexport function encodeSessionCookiesDecoded(\n environmentRef: string,\n session: StoredSession,\n): SessionCookieWrite {\n const { set, clear } = encodeSessionCookies(environmentRef, session);\n return {\n set: set.map(({ name, value }) => ({ name, value: decodeURIComponent(value) })),\n clear,\n };\n}\n\n/**\n * Read the session back from a cookie getter: the base name wins; otherwise\n * `.0`, `.1`, … are concatenated until the first missing chunk. Tolerant:\n * malformed URI escapes / JSON / shape → null (treated as \"no session\").\n *\n * Accepts BOTH value forms. Next's cookie stores percent-DECODE values once\n * on read, so server-side callers (pbServer adapter, middleware) hand us the\n * already-decoded JSON; raw readers (document.cookie) hand us the encoded\n * form. The raw (already-decoded) parse runs FIRST because it is\n * unambiguous — an encoded value can never raw-parse as JSON\n * (encodeURIComponent escapes `{` itself, so it starts with `%7B`) — whereas\n * decode-first would silently corrupt a literal `%XX` run inside a token\n * (decodeURIComponent('%41') → 'A', no throw) or throw on an invalid escape.\n */\nexport function decodeSessionCookies(\n get: (name: string) => string | undefined,\n environmentRef: string,\n): StoredSession | null {\n const base = sessionCookieName(environmentRef);\n let encoded = get(base);\n if (encoded === undefined) {\n let joined = '';\n for (let i = 0; ; i++) {\n const part = get(`${base}.${i}`);\n if (part === undefined) break;\n joined += part;\n }\n if (joined === '') return null;\n encoded = joined;\n }\n const raw = parseStoredSession(encoded);\n if (raw !== null) return raw;\n try {\n return parseStoredSession(decodeURIComponent(encoded));\n } catch {\n return null;\n }\n}\n\n/** JSON-parse + shape-check one candidate value; malformed → null. */\nfunction parseStoredSession(json: string): StoredSession | null {\n try {\n const parsed: unknown = JSON.parse(json);\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.a === 'string' && typeof obj.r === 'string' && typeof obj.e === 'number') {\n return { accessToken: obj.a, refreshToken: obj.r, expiresAt: obj.e };\n }\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Names a writer must delete before/while writing: the base name (when\n * present) plus every existing `.N` chunk, probed in order until the first\n * missing one (writes are always a contiguous `.0..n` run, so a gap means\n * the end of palbe-owned chunks).\n */\nexport function clearedSessionCookieNames(\n environmentRef: string,\n present: (name: string) => boolean,\n): string[] {\n const base = sessionCookieName(environmentRef);\n const names: string[] = [];\n if (present(base)) names.push(base);\n for (let i = 0; ; i++) {\n const chunk = `${base}.${i}`;\n if (!present(chunk)) break;\n names.push(chunk);\n }\n return names;\n}\n\n/**\n * Parse the canonical chunk index out of `palbe-session-<ref>.N`; non-chunk\n * names (the bare base, a foreign ref, junk suffixes like `.x` / `.01` /\n * `.-1`) → null. The index is a non-negative integer with NO leading zeros\n * (the encoder emits `.0`, `.1`, … verbatim), so `String(n) === suffix`\n * round-trips it exactly.\n */\nfunction chunkIndex(base: string, name: string): number | null {\n if (!name.startsWith(`${base}.`)) return null;\n const suffix = name.slice(base.length + 1);\n if (!/^\\d+$/.test(suffix)) return null;\n const idx = Number.parseInt(suffix, 10);\n return String(idx) === suffix ? idx : null;\n}\n\n/**\n * getAll-powered orphan sweep — the deletion list a writer with the FULL\n * cookie jar should clear, catching crash-orphans that presence-probing\n * (clearedSessionCookieNames, which stops at the first gap) CANNOT see.\n *\n * Given every cookie name in the jar, returns the `palbe-session-<ref>`-family\n * names that are NOT part of a single valid session:\n * - A valid session is EITHER the bare base alone, OR the contiguous chunk\n * run `.0, .1, …, n` (starting at `.0`, no gaps).\n * - Any `.N` beyond the first gap from `.0` is an orphan (incl. ALL chunks\n * when `.0` is absent — no run exists).\n * - The base coexisting with ANY chunk is inconsistent (the contract forbids\n * it; decode lets the base win and silently strands the chunks): the base\n * is kept, the chunks are swept (a fresh write replaces the base in place).\n *\n * Only canonical `.N` names count; foreign refs and malformed suffixes are\n * ignored. Output order: base (if inconsistent) first, then chunks ascending.\n */\nexport function orphanSessionCookieNames(environmentRef: string, allNames: string[]): string[] {\n const base = sessionCookieName(environmentRef);\n const hasBase = allNames.includes(base);\n const indices = new Set<number>();\n for (const name of allNames) {\n const idx = chunkIndex(base, name);\n if (idx !== null) indices.add(idx);\n }\n if (indices.size === 0) return []; // unchunked (base alone) or no palbe cookies\n\n // Length of the contiguous run from .0 (0 when .0 is missing).\n let runLen = 0;\n while (indices.has(runLen)) runLen++;\n\n const orphans: string[] = [];\n // Base coexisting with chunks: base wins on decode → chunks are stale.\n // Sweep the base too only when there are NO chunks behind the run to keep\n // (the contract forbids the mix; clearing chunks alone resolves it, but if\n // the run itself is invalid—no .0—the base is the real session and stays).\n // Decision: when base coexists with ANY chunk, the chunks are orphans; the\n // base is the live session and is kept — UNLESS the chunk set forms a valid\n // run AND there is also a gap-orphan, in which case both base+gap-orphan are\n // inconsistent leftovers. Concretely: keep it simple and SAFE — the base is\n // only swept when it sits alongside a partial/gapped chunk set (a crash mid\n // base→chunk migration), never when chunks form a clean run the base shadows.\n const everyChunkContiguous = indices.size === runLen && runLen > 0;\n if (hasBase && !everyChunkContiguous) orphans.push(base);\n\n for (const idx of [...indices].sort((a, b) => a - b)) {\n if (idx >= runLen) orphans.push(`${base}.${idx}`);\n }\n // Base + clean contiguous run: base wins on decode, chunks are stranded.\n if (hasBase && everyChunkContiguous) {\n for (let i = 0; i < runLen; i++) orphans.push(`${base}.${i}`);\n }\n return orphans;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/api-key.ts
|
|
8
|
+
var API_KEY_RE = /^pb_([a-z0-9]+)_[cs][A-Za-z0-9]{20}$/;
|
|
9
|
+
var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
|
|
10
|
+
function environmentRefFromApiKey(apiKey) {
|
|
11
|
+
const m = API_KEY_RE.exec(apiKey);
|
|
12
|
+
return m ? m[1] ?? "" : "";
|
|
13
|
+
}
|
|
14
|
+
function environmentRefFromPublishableApiKey(apiKey) {
|
|
15
|
+
const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);
|
|
16
|
+
return match ? match[1] ?? "" : "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
__export,
|
|
21
|
+
environmentRefFromApiKey,
|
|
22
|
+
environmentRefFromPublishableApiKey
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=chunk-S7NAY3MW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-key.ts"],"sourcesContent":["/**\n * `pb_<environmentRef>_<scope><random>` → environmentRef ('' if malformed).\n * Utility for callers that only have a raw key. App-bound runtime storage and\n * palbe/next cookie naming use the explicit `PalbeConfig.environmentRef` emitted\n * by `palbe-gen`.\n *\n * The key is shape-validated (prefix `pb`, non-empty environmentRef, and a\n * `<scope><20-base62-random>` secret segment where scope ∈ {c,s}) before a ref\n * is returned. A structurally-invalid key returns '' so session storage stays\n * UNSCOPED rather than getting bound to a partially-parsed ref — a loose parse\n * (e.g. `pb_victim_cXX`) must not scope persisted sessions to `victim` and\n * hydrate a session belonging to a different, real key. The backend remains the\n * authority that rejects a bad key on the first request; this keeps the client's\n * local session-storage scoping honest. (Audit finding apikey-ref-mismatch.)\n */\nconst API_KEY_RE = /^pb_([a-z0-9]+)_[cs][A-Za-z0-9]{20}$/;\nconst PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;\n\nexport function environmentRefFromApiKey(apiKey: string): string {\n const m = API_KEY_RE.exec(apiKey);\n return m ? (m[1] ?? '') : '';\n}\n\n/** Internal app-runtime parser: browser config accepts publishable keys only. */\nexport function environmentRefFromPublishableApiKey(apiKey: string): string {\n const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);\n return match ? (match[1] ?? '') : '';\n}\n"],"mappings":";;;;;;;AAeA,IAAM,aAAa;AACnB,IAAM,yBAAyB;AAExB,SAAS,yBAAyB,QAAwB;AAC/D,QAAM,IAAI,WAAW,KAAK,MAAM;AAChC,SAAO,IAAK,EAAE,CAAC,KAAK,KAAM;AAC5B;AAGO,SAAS,oCAAoC,QAAwB;AAC1E,QAAM,QAAQ,uBAAuB,KAAK,MAAM;AAChD,SAAO,QAAS,MAAM,CAAC,KAAK,KAAM;AACpC;","names":[]}
|
package/dist/gen/cli.cjs
CHANGED
|
@@ -33,6 +33,13 @@ var import_node_path = require("path");
|
|
|
33
33
|
var import_node_process = __toESM(require("process"), 1);
|
|
34
34
|
var import_node_util = require("util");
|
|
35
35
|
|
|
36
|
+
// src/api-key.ts
|
|
37
|
+
var PUBLISHABLE_API_KEY_RE = /^pb_([a-z0-9]+)_c[A-Za-z0-9]{20}$/;
|
|
38
|
+
function environmentRefFromPublishableApiKey(apiKey) {
|
|
39
|
+
const match = PUBLISHABLE_API_KEY_RE.exec(apiKey);
|
|
40
|
+
return match ? match[1] ?? "" : "";
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
// src/gen/emitter.ts
|
|
37
44
|
function opSegments(opId) {
|
|
38
45
|
return opId.split(".").filter((p) => p !== "");
|
|
@@ -121,7 +128,6 @@ function emitTypeScript(ops, cfg) {
|
|
|
121
128
|
return b;
|
|
122
129
|
}
|
|
123
130
|
function emitTSConfigure(cfg) {
|
|
124
|
-
const branch = cfg.branch === "" ? "main" : cfg.branch;
|
|
125
131
|
let b = "__configure({\n";
|
|
126
132
|
b += ` url: ${tsStringLit(cfg.url)},
|
|
127
133
|
`;
|
|
@@ -129,7 +135,7 @@ function emitTSConfigure(cfg) {
|
|
|
129
135
|
`;
|
|
130
136
|
b += ` appId: ${tsStringLit(cfg.appId)},
|
|
131
137
|
`;
|
|
132
|
-
b += `
|
|
138
|
+
b += ` environmentRef: ${tsStringLit(cfg.environmentRef)},
|
|
133
139
|
`;
|
|
134
140
|
const oauth = renderTSOAuth(cfg.oauth);
|
|
135
141
|
if (oauth !== "") {
|
|
@@ -995,14 +1001,33 @@ function readWebConfig(dir) {
|
|
|
995
1001
|
if (value.trim() === "") {
|
|
996
1002
|
throw new Error(`${path} is missing nonempty required field ${field}`);
|
|
997
1003
|
}
|
|
998
|
-
return value;
|
|
1004
|
+
return value.trim();
|
|
999
1005
|
};
|
|
1000
1006
|
const cfg = {
|
|
1001
1007
|
url: required("base_url"),
|
|
1002
1008
|
apiKey: required("api_key"),
|
|
1003
1009
|
appId: required("app_id"),
|
|
1004
|
-
|
|
1010
|
+
environmentRef: required("environment_ref")
|
|
1005
1011
|
};
|
|
1012
|
+
const keyEnvironmentRef = environmentRefFromPublishableApiKey(cfg.apiKey);
|
|
1013
|
+
if (keyEnvironmentRef === "") {
|
|
1014
|
+
throw new Error(`${path} api_key does not contain a valid publishable Environment identity`);
|
|
1015
|
+
}
|
|
1016
|
+
if (cfg.environmentRef !== keyEnvironmentRef) {
|
|
1017
|
+
throw new Error(`${path} environment_ref must match the Environment identity in api_key`);
|
|
1018
|
+
}
|
|
1019
|
+
let configURL;
|
|
1020
|
+
try {
|
|
1021
|
+
configURL = new URL(cfg.url);
|
|
1022
|
+
} catch {
|
|
1023
|
+
throw new Error(`${path} base_url must be a valid HTTPS URL`);
|
|
1024
|
+
}
|
|
1025
|
+
if (configURL.protocol !== "https:") {
|
|
1026
|
+
throw new Error(`${path} base_url must be a valid HTTPS URL`);
|
|
1027
|
+
}
|
|
1028
|
+
if (configURL.hostname.endsWith(".palbase.studio") && configURL.hostname.split(".")[0] !== cfg.environmentRef) {
|
|
1029
|
+
throw new Error(`${path} base_url must match environment_ref for palbase.studio`);
|
|
1030
|
+
}
|
|
1006
1031
|
const oauthRaw = obj.oauth;
|
|
1007
1032
|
if (oauthRaw && typeof oauthRaw === "object") {
|
|
1008
1033
|
const oauth = {};
|
|
@@ -1057,7 +1082,7 @@ async function generateOnce(dir, outFile, url, out) {
|
|
|
1057
1082
|
if (url !== void 0) {
|
|
1058
1083
|
const origin = url.replace(/\/+$/, "");
|
|
1059
1084
|
specText = await fetchSpec(`${origin}/openapi.json`);
|
|
1060
|
-
cfg = { url: origin
|
|
1085
|
+
cfg = { ...readWebConfig(dir), url: origin };
|
|
1061
1086
|
} else {
|
|
1062
1087
|
cfg = readWebConfig(dir);
|
|
1063
1088
|
const specPath = (0, import_node_path.join)(dir, "openapi.json");
|
|
@@ -1145,10 +1170,10 @@ async function* intervalTicks(ms, signal) {
|
|
|
1145
1170
|
yield;
|
|
1146
1171
|
}
|
|
1147
1172
|
}
|
|
1148
|
-
async function runWatch(outFile, url, out) {
|
|
1173
|
+
async function runWatch(dir, outFile, url, out) {
|
|
1149
1174
|
const origin = (url ?? "http://localhost:4003").replace(/\/+$/, "");
|
|
1150
1175
|
const specURL = `${origin}/openapi.json`;
|
|
1151
|
-
const cfg = { url: origin
|
|
1176
|
+
const cfg = { ...readWebConfig(dir), url: origin };
|
|
1152
1177
|
try {
|
|
1153
1178
|
writeGenerated(parseOpenAPI(await fetchSpec(specURL)), cfg, outFile, out);
|
|
1154
1179
|
} catch (err) {
|
|
@@ -1201,7 +1226,7 @@ async function main(argv, out = stdoutWriter, errOut = stderrWriter) {
|
|
|
1201
1226
|
}
|
|
1202
1227
|
try {
|
|
1203
1228
|
if (values.watch) {
|
|
1204
|
-
await runWatch(values.out, values.url, out);
|
|
1229
|
+
await runWatch(values.dir, values.out, values.url, out);
|
|
1205
1230
|
} else {
|
|
1206
1231
|
await generateOnce(values.dir, values.out, values.url, out);
|
|
1207
1232
|
}
|