@agent-native/core 0.84.59 → 0.84.61
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +49 -14
- package/corpus/core/src/db/client.ts +13 -1
- package/corpus/core/src/server/ssr-handler.ts +14 -16
- package/corpus/core/src/sharing/access.ts +79 -5
- package/corpus/templates/clips/app/i18n/ar-SA.ts +7 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +8 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +7 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +7 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +6 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +8 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +7 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +7 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +5 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +5 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +174 -28
- package/corpus/templates/clips/app/routes/record.tsx +74 -6
- package/corpus/templates/plan/changelog/2026-07-02-fixed-hosted-visual-plans-getting-stuck-loading-when-a-datab.md +6 -0
- package/corpus/templates/plan/server/plugins/db.ts +25 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +42 -15
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +11 -1
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +14 -16
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +54 -5
- package/dist/sharing/access.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"ssr-handler.d.ts","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"AA8CA,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAooBpC;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,2FAgE5E"}
|
|
@@ -195,8 +195,7 @@ function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
|
|
|
195
195
|
* Extract the plain JS body from a `<script ...>body</script>` string.
|
|
196
196
|
* Returns `null` if the input is falsy or has no recognisable `</script>` end.
|
|
197
197
|
* Used to compute the sha256 hash of framework-injected inline scripts so the
|
|
198
|
-
* hash can be listed in
|
|
199
|
-
* `'unsafe-inline'`.
|
|
198
|
+
* hash can be listed in app-owned `script-src` CSP directives.
|
|
200
199
|
*/
|
|
201
200
|
function extractScriptBody(scriptTag) {
|
|
202
201
|
if (!scriptTag)
|
|
@@ -394,13 +393,10 @@ function augmentExistingReportOnlyCspForFrameworkScripts(policy, options) {
|
|
|
394
393
|
*
|
|
395
394
|
* A third directive, `script-src`, is emitted via `Content-Security-Policy-
|
|
396
395
|
* Report-Only` rather than enforced when the app has no existing document CSP.
|
|
397
|
-
* The framework injects
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* Google Tag Manager / GA4 from `GA_CSP_SCRIPT_HOSTS`. All of those are listed
|
|
402
|
-
* here so the report-only policy reflects the code the framework itself injects
|
|
403
|
-
* instead of reporting a violation on every page load.
|
|
396
|
+
* The framework injects inline scripts for analytics, Sentry, and template
|
|
397
|
+
* setup, and hosted apps need Google Tag Manager to load without noisy CSP
|
|
398
|
+
* diagnostics. The report-only policy is intentionally permissive for scripts:
|
|
399
|
+
* it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.
|
|
404
400
|
*
|
|
405
401
|
* If an app or host already sends an enforced CSP with `script-src`,
|
|
406
402
|
* `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only
|
|
@@ -425,20 +421,22 @@ function applyDocumentCsp(headers, sentryScript) {
|
|
|
425
421
|
return;
|
|
426
422
|
if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === "1")
|
|
427
423
|
return;
|
|
428
|
-
// script-src as Report-Only:
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
// itself injects, so listing them keeps the report-only policy from flagging
|
|
432
|
-
// GA on every page load (and keeps it safe to graduate to enforcement).
|
|
433
|
-
// Template theme-init hashes are NOT included here — see function comment.
|
|
424
|
+
// script-src as Report-Only: keep this deliberately loose so the framework's
|
|
425
|
+
// injected analytics and template bootstrap scripts do not look blocked in
|
|
426
|
+
// browser diagnostics.
|
|
434
427
|
const sentryBody = extractScriptBody(sentryScript);
|
|
435
428
|
const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;
|
|
436
429
|
const gaInlineBody = getGaInlineConfigScriptBody();
|
|
437
430
|
const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;
|
|
438
431
|
const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];
|
|
439
|
-
const gaScriptSrcTokens = [
|
|
432
|
+
const gaScriptSrcTokens = [
|
|
433
|
+
"'unsafe-inline'",
|
|
434
|
+
...(gaHash ? [gaHash] : []),
|
|
435
|
+
...gaHosts,
|
|
436
|
+
];
|
|
440
437
|
const scriptSrcTokens = [
|
|
441
438
|
"'self'",
|
|
439
|
+
"'unsafe-inline'",
|
|
442
440
|
...(sentryHash ? [sentryHash] : []),
|
|
443
441
|
...(gaHash ? [gaHash] : []),
|
|
444
442
|
...gaHosts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-handler.js","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineEventHandler } from \"h3\";\n/**\n * Shared SSR catch-all handler for React Router framework mode.\n *\n * Templates wire this up via:\n *\n * // server/routes/[...page].get.ts\n * import { createH3SSRHandler } from \"@agent-native/core/server/ssr-handler\";\n * export default createH3SSRHandler(\n * () => import(\"virtual:react-router/server-build\"),\n * );\n *\n * The `getBuild` callback MUST live in the template's own source so Vite's\n * @react-router/dev plugin can resolve the `virtual:` module. Pulling the\n * import into core (e.g. via a re-export) puts it in node_modules where\n * Vite's SSR externalizer leaves it untouched and Node's ESM loader rejects\n * the unknown scheme — silently 302'ing every request to \"/\".\n */\nimport { createRequestHandler } from \"react-router\";\n\nimport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_PATH,\n} from \"../shared/cache-control.js\";\nimport {\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n withAgentNativeSocialImageCacheBuster,\n} from \"../shared/social-meta.js\";\nimport {\n GA_CSP_CONNECT_HOSTS,\n GA_CSP_IMG_HOSTS,\n GA_CSP_SCRIPT_HOSTS,\n getGaInlineConfigScriptBody,\n} from \"./analytics.js\";\nimport {\n getAppBasePathFromViteEnv,\n stripAppBasePath as canonicalStripAppBasePath,\n} from \"./app-base-path.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { computeInlineScriptHash } from \"./security-headers.js\";\nimport { getSentryClientConfigScript } from \"./sentry-config.js\";\n\nexport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_HEADER,\n DEFAULT_SSR_CACHE_CONTROL,\n} from \"../shared/cache-control.js\";\n\nfunction getAppBasePath(): string {\n return getAppBasePathFromViteEnv();\n}\n\nfunction stripAppBasePath(pathname: string): string {\n return canonicalStripAppBasePath(pathname, getAppBasePath());\n}\n\nfunction stripBasePath(pathname: string, basePath: string): string {\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\nfunction requestWithPathname(\n request: Request,\n pathname: string,\n basePath: string,\n): Request {\n const url = new URL(request.url);\n let changed = false;\n if (basePath && pathname === \"/__manifest\") {\n const paths = url.searchParams.get(\"paths\");\n if (paths) {\n const strippedPaths = paths\n .split(\",\")\n .map((path) => stripBasePath(path, basePath))\n .join(\",\");\n if (strippedPaths !== paths) {\n url.searchParams.set(\"paths\", strippedPaths);\n changed = true;\n }\n }\n }\n if (url.pathname !== pathname) {\n url.pathname = pathname;\n changed = true;\n }\n if (!changed) return request;\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n };\n if (request.body && ![\"GET\", \"HEAD\"].includes(request.method.toUpperCase())) {\n init.body = request.body;\n init.duplex = \"half\";\n }\n return new Request(url, init);\n}\n\nfunction prefixMountedPath(path: string, basePath: string): string {\n if (!basePath || !path.startsWith(\"/\") || path.startsWith(\"//\")) return path;\n if (path === basePath || path.startsWith(`${basePath}/`)) return path;\n return `${basePath}${path}`;\n}\n\nfunction prefixMountedHtml(html: string, basePath: string): string {\n if (!basePath) return html;\n return html\n .replace(\n /\\b(href|src|action|formaction|poster)=([\"'])(\\/(?!\\/)[^\"']*)\\2/g,\n (_match, attr: string, quote: string, path: string) =>\n `${attr}=${quote}${prefixMountedPath(path, basePath)}${quote}`,\n )\n .replace(/url\\(([\"']?)(\\/(?!\\/)[^)'\" ]+)\\1\\)/g, (_match, quote, path) => {\n const q = quote || \"\";\n return `url(${q}${prefixMountedPath(path, basePath)}${q})`;\n });\n}\n\nfunction injectHeadScript(html: string, script: string | null): string {\n if (!script) return html;\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);\n}\n\nconst OG_IMAGE_META_RE = /<meta\\b(?=[^>]*\\bproperty=([\"'])og:image\\1)[^>]*>/i;\nconst TWITTER_CARD_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:card\\1)[^>]*>/i;\nconst TWITTER_IMAGE_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:image\\1)[^>]*>/i;\n\nfunction defaultSocialImageUrl(requestUrl: string, basePath: string): string {\n return withAgentNativeSocialImageCacheBuster(\n new URL(\n prefixMountedPath(AGENT_NATIVE_SOCIAL_IMAGE_PATH, basePath),\n requestUrl,\n ).toString(),\n );\n}\n\nfunction injectDefaultSocialImageMeta(html: string, imageUrl: string): string {\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n\n const hasAnySocialImage =\n OG_IMAGE_META_RE.test(html) || TWITTER_IMAGE_META_RE.test(html);\n const tags: string[] = [];\n\n if (!hasAnySocialImage) {\n tags.push(`<meta property=\"og:image\" content=\"${imageUrl}\">`);\n tags.push(`<meta property=\"og:image:secure_url\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta property=\"og:image:type\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_TYPE}\">`,\n );\n tags.push(\n `<meta property=\"og:image:width\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_WIDTH}\">`,\n );\n tags.push(\n `<meta property=\"og:image:height\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT}\">`,\n );\n tags.push(\n `<meta property=\"og:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n if (!TWITTER_CARD_META_RE.test(html)) {\n tags.push(`<meta name=\"twitter:card\" content=\"summary_large_image\">`);\n }\n if (!hasAnySocialImage) {\n tags.push(`<meta name=\"twitter:image\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta name=\"twitter:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n\n if (tags.length === 0) return html;\n return html.slice(0, headCloseIdx) + tags.join(\"\") + html.slice(headCloseIdx);\n}\n\nfunction isSsrHtmlOrDataResponse(\n headers: Headers,\n status: number,\n pathname: string,\n): boolean {\n if (status < 200 || status >= 400) return false;\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (contentType.includes(\"text/html\")) return true;\n return pathname.endsWith(\".data\") && contentType.includes(\"text/x-script\");\n}\n\n/**\n * Apply the SSR cache policy to the response headers.\n *\n * ┌──────────────────────────────────────────────────────────────────────────┐\n * │ SSR IS A PUBLIC, HARD-CDN-CACHED SHELL — SERVED IDENTICALLY TO EVERYONE. │\n * │ │\n * │ Every SSR HTML / React Router `.data` response gets the same public │\n * │ stale-while-revalidate policy for ALL visitors, authenticated or not, so │\n * │ the edge serves one shared copy and never stampedes origin. │\n * │ │\n * │ DO NOT reintroduce per-user / cookie-based cache variation here (no │\n * │ `private`, no `no-store`, no `Vary: Cookie`, no \"authenticated → don't │\n * │ cache\" branch). That makes pages uncacheable for every logged-in visitor, │\n * │ which is slow and expensive — exactly the regression this guardrail │\n * │ prevents. The reason it is SAFE to hard-cache is that the SSR response is │\n * │ impersonal: `createH3SSRHandler` renders without reading the request's │\n * │ session/cookies, so there is no per-user data baked into the HTML. ALL │\n * │ per-user state (who's logged in, private records, access checks) is │\n * │ resolved CLIENT-SIDE after load. Keep it that way: if you need the SSR │\n * │ output to differ per user, the fix is to move that work client-side, not │\n * │ to disable caching here. │\n * └──────────────────────────────────────────────────────────────────────────┘\n */\nfunction applyDefaultSsrCacheHeader(\n headers: Headers,\n status: number,\n pathname: string,\n) {\n if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;\n\n // Netlify Functions/proxies are not cached by default. Set all three cache\n // headers: Cache-Control for browsers, CDN-Cache-Control for generic CDNs,\n // and Netlify-CDN-Cache-Control (with durable) so Netlify's shared cache\n // actually serves SSR HTML/.data from the edge instead of forwarding every\n // request to origin — for every visitor, authenticated or not.\n for (const [name, value] of Object.entries(DEFAULT_SSR_CACHE_HEADERS)) {\n headers.set(name, value);\n }\n}\n\nfunction applyDefaultSpeculationRulesHeader(\n headers: Headers,\n status: number,\n basePath: string,\n) {\n if (status < 200 || status >= 400) return;\n if (headers.has(\"speculation-rules\")) return;\n\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (!contentType.includes(\"text/html\")) return;\n\n // Cloudflare Speed Brain injects its own Speculation-Rules header when the\n // origin omits one. Those browser prefetches carry `Sec-Purpose: prefetch`,\n // and Cloudflare refuses cache-ineligible dynamic pages with a 503 before\n // the request can reach Netlify/origin. We publish an explicit no-op ruleset\n // by default so Cloudflare does not inject its edge prefetch rules. Preserve\n // an app-provided Speculation-Rules header above if a template deliberately\n // owns this behavior.\n const rulesPath = prefixMountedPath(DEFAULT_SPECULATION_RULES_PATH, basePath);\n headers.set(\"speculation-rules\", `\"${rulesPath}\"`);\n}\n\n/**\n * Extract the plain JS body from a `<script ...>body</script>` string.\n * Returns `null` if the input is falsy or has no recognisable `</script>` end.\n * Used to compute the sha256 hash of framework-injected inline scripts so the\n * hash can be listed in the `script-src` CSP directive without relying on\n * `'unsafe-inline'`.\n */\nfunction extractScriptBody(scriptTag: string | null): string | null {\n if (!scriptTag) return null;\n const start = scriptTag.indexOf(\">\") + 1;\n const end = scriptTag.lastIndexOf(\"</script>\");\n if (start <= 0 || end < start) return null;\n return scriptTag.slice(start, end);\n}\n\ntype CspDirective = {\n name: string;\n tokens: string[];\n};\n\nconst CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([\n \"base-uri\",\n \"block-all-mixed-content\",\n \"child-src\",\n \"connect-src\",\n \"default-src\",\n \"fenced-frame-src\",\n \"font-src\",\n \"form-action\",\n \"frame-ancestors\",\n \"frame-src\",\n \"img-src\",\n \"manifest-src\",\n \"media-src\",\n \"navigate-to\",\n \"object-src\",\n \"plugin-types\",\n \"prefetch-src\",\n \"referrer\",\n \"reflected-xss\",\n \"require-sri-for\",\n \"require-trusted-types-for\",\n \"report-to\",\n \"report-uri\",\n \"sandbox\",\n \"script-src\",\n \"script-src-attr\",\n \"script-src-elem\",\n \"style-src\",\n \"style-src-attr\",\n \"style-src-elem\",\n \"trusted-types\",\n \"upgrade-insecure-requests\",\n \"webrtc\",\n \"worker-src\",\n]);\n\nfunction hasCommaJoinedCspPolicies(policy: string): boolean {\n let commaIndex = policy.indexOf(\",\");\n while (commaIndex !== -1) {\n const afterComma = policy.slice(commaIndex + 1);\n const directive = /^\\s+([a-z][a-z0-9-]*)(?=\\s|;|$)/i.exec(afterComma)?.[1];\n if (\n directive &&\n CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())\n ) {\n return true;\n }\n commaIndex = policy.indexOf(\",\", commaIndex + 1);\n }\n return false;\n}\n\nfunction parseCsp(policy: string): CspDirective[] {\n return policy\n .split(\";\")\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [name = \"\", ...tokens] = part.split(/\\s+/);\n return { name: name.toLowerCase(), tokens };\n })\n .filter((directive) => directive.name);\n}\n\nfunction serializeCsp(directives: CspDirective[]): string {\n return directives\n .map((directive) =>\n [directive.name, ...directive.tokens].filter(Boolean).join(\" \"),\n )\n .join(\"; \");\n}\n\nfunction appendCspTokens(\n tokens: string[],\n additions: readonly string[],\n): string[] {\n if (!additions.length) return tokens;\n const next = tokens.filter((token) => token !== \"'none'\");\n const seen = new Set(next);\n for (const token of additions) {\n if (!token || seen.has(token)) continue;\n next.push(token);\n seen.add(token);\n }\n return next;\n}\n\nfunction findCspDirective(\n directives: CspDirective[],\n name: string,\n): CspDirective | undefined {\n return directives.find((directive) => directive.name === name);\n}\n\nfunction appendToExistingOrDefaultCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n if (!additions.length) return;\n const existing = findCspDirective(directives, name);\n if (existing) {\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc) return;\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n}\n\nfunction appendToExistingCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n const existing = findCspDirective(directives, name);\n if (!existing) return;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n}\n\nfunction ensureCspDirective(\n directives: CspDirective[],\n name: string,\n tokens: readonly string[],\n): void {\n if (findCspDirective(directives, name)) return;\n directives.push({ name, tokens: [...tokens] });\n}\n\nfunction hasStrictNonceScriptPolicy(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) => token === \"'strict-dynamic'\" || token.startsWith(\"'nonce-\"),\n );\n}\n\nfunction appendToScriptCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): boolean {\n const existing = findCspDirective(directives, name);\n if (existing) {\n if (hasStrictNonceScriptPolicy(existing.tokens)) return false;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return true;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {\n return false;\n }\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n return true;\n}\n\nfunction appendToEffectiveScriptElementCspDirective(\n directives: CspDirective[],\n additions: readonly string[],\n): boolean {\n const scriptSrcElem = findCspDirective(directives, \"script-src-elem\");\n if (scriptSrcElem) {\n if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens)) return false;\n scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);\n return true;\n }\n\n return appendToScriptCspDirective(directives, \"script-src\", additions);\n}\n\nfunction augmentExistingEnforcedCspForFrameworkScripts(\n policy: string,\n options: {\n gaScriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n // Multiple CSP headers are surfaced by Headers.get() as one comma-joined\n // string. CSP is not a comma-list header, so serializing a parsed combined\n // value would turn two policies into one invalid policy. Leave those headers\n // app-owned; a comma inside a source/report URL is still safe to parse.\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n if (options.gaEnabled) {\n const addedScriptElement = appendToEffectiveScriptElementCspDirective(\n directives,\n options.gaScriptSrcTokens,\n );\n if (addedScriptElement) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n }\n\n ensureCspDirective(directives, \"object-src\", [\"'none'\"]);\n ensureCspDirective(directives, \"base-uri\", [\"'self'\"]);\n\n return serializeCsp(directives);\n}\n\nfunction augmentExistingReportOnlyCspForFrameworkScripts(\n policy: string,\n options: {\n scriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n appendToExistingOrDefaultCspDirective(\n directives,\n \"script-src\",\n options.scriptSrcTokens,\n );\n appendToExistingCspDirective(\n directives,\n \"script-src-elem\",\n options.scriptSrcTokens,\n );\n\n if (options.gaEnabled) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n\n return serializeCsp(directives);\n}\n\n/**\n * Apply a Content-Security-Policy header to HTML document responses.\n *\n * Two directives are always enforced in production:\n *\n * - `object-src 'none'` — disables Flash / Java / PDF plugin execution,\n * which are a reliable code-execution vector even in modern browsers.\n * - `base-uri 'self'` — prevents a `<base href=\"...\">` injection from\n * hijacking all relative URLs in the document (a common attack target when\n * user-controlled content reaches the HTML).\n *\n * A third directive, `script-src`, is emitted via `Content-Security-Policy-\n * Report-Only` rather than enforced when the app has no existing document CSP.\n * The framework injects deterministic inline scripts (the Sentry config block,\n * whose hash is computed once at process startup from the resolved env vars,\n * and — when `GA_MEASUREMENT_ID` is set — the gtag config block, whose hash is\n * derived from the same string `wrapWithAnalytics` embeds). It also loads\n * Google Tag Manager / GA4 from `GA_CSP_SCRIPT_HOSTS`. All of those are listed\n * here so the report-only policy reflects the code the framework itself injects\n * instead of reporting a violation on every page load.\n *\n * If an app or host already sends an enforced CSP with `script-src`,\n * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only\n * GA-specific allowances into existing host/hash policies. Strict nonce or\n * `strict-dynamic` script policies stay app-owned because blindly appending\n * hashes or hosts would widen the policy without reliably loading our injected\n * scripts.\n *\n * Templates additionally render a theme-init inline script whose exact content\n * varies by template (default theme param, custom docs variant, etc.) and which\n * is rendered by React Router, not this handler, so its hash is not available\n * here. Shipping script-src as Report-Only surfaces the remaining violations\n * without breaking template customisations; teams can graduate to enforcement\n * once their hashes are enumerated.\n *\n * Skipped in development (`NODE_ENV !== 'production'`) so HMR eval and Vite\n * dev-server injects are never blocked. Set `AGENT_NATIVE_DISABLE_DOC_CSP=1`\n * to opt out in production for a template with exotic needs.\n */\nfunction applyDocumentCsp(headers: Headers, sentryScript: string | null): void {\n if (process.env.NODE_ENV !== \"production\") return;\n if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === \"1\") return;\n\n // script-src as Report-Only: list 'self', the framework-injected inline\n // script hashes (Sentry config + gtag config), and the Google Analytics /\n // Tag Manager loader hosts. These are exactly the scripts the framework\n // itself injects, so listing them keeps the report-only policy from flagging\n // GA on every page load (and keeps it safe to graduate to enforcement).\n // Template theme-init hashes are NOT included here — see function comment.\n const sentryBody = extractScriptBody(sentryScript);\n const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;\n const gaInlineBody = getGaInlineConfigScriptBody();\n const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;\n const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];\n const gaScriptSrcTokens = [...(gaHash ? [gaHash] : []), ...gaHosts];\n const scriptSrcTokens = [\n \"'self'\",\n ...(sentryHash ? [sentryHash] : []),\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n\n const cspAugmentOptions = {\n scriptSrcTokens,\n gaScriptSrcTokens,\n gaEnabled: Boolean(gaInlineBody),\n };\n const existing = headers.get(\"content-security-policy\") ?? \"\";\n if (!existing) {\n headers.set(\n \"content-security-policy\",\n \"object-src 'none'; base-uri 'self'\",\n );\n } else {\n headers.set(\n \"content-security-policy\",\n augmentExistingEnforcedCspForFrameworkScripts(\n existing,\n cspAugmentOptions,\n ),\n );\n }\n\n const scriptSrc = `script-src ${scriptSrcTokens.join(\" \")}`;\n const existingRo = headers.get(\"content-security-policy-report-only\") ?? \"\";\n if (!existingRo) {\n headers.set(\"content-security-policy-report-only\", scriptSrc);\n } else {\n headers.set(\n \"content-security-policy-report-only\",\n augmentExistingReportOnlyCspForFrameworkScripts(\n existingRo,\n cspAugmentOptions,\n ),\n );\n }\n}\n\nfunction isFrameworkOrAssetPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/.well-known/\") ||\n pathname.startsWith(\"/_agent_native/\") ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname.startsWith(\"/api/\") ||\n pathname.startsWith(\"/@vite/\") ||\n pathname.startsWith(\"/@id/\") ||\n pathname.startsWith(\"/@fs/\") ||\n pathname === \"/@react-refresh\" ||\n pathname === \"/__vite_ping\" ||\n pathname === \"/__open-in-editor\" ||\n pathname === \"/favicon.ico\" ||\n pathname === \"/favicon.png\" ||\n (/\\.\\w+$/.test(pathname) && !pathname.endsWith(\".data\"))\n );\n}\n\nasync function rewriteMountedResponse(\n response: Response,\n basePath: string,\n pathname: string,\n requestUrl: string,\n): Promise<Response> {\n const sentryClientConfigScript = getSentryClientConfigScript();\n const headers = new Headers(response.headers);\n applyDefaultSsrCacheHeader(headers, response.status, pathname);\n applyDefaultSpeculationRulesHeader(headers, response.status, basePath);\n\n const location = headers.get(\"location\");\n if (location?.startsWith(\"/\") && !location.startsWith(\"//\")) {\n headers.set(\"location\", prefixMountedPath(location, basePath));\n }\n\n const contentType = headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"text/html\") || !response.body) {\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const html = await response.text();\n headers.delete(\"content-length\");\n applyDocumentCsp(headers, sentryClientConfigScript);\n return new Response(\n injectHeadScript(\n injectDefaultSocialImageMeta(\n prefixMountedHtml(html, basePath),\n defaultSocialImageUrl(requestUrl, basePath),\n ),\n sentryClientConfigScript,\n ),\n {\n status: response.status,\n statusText: response.statusText,\n headers,\n },\n );\n}\n\n/**\n * Create an h3 catch-all that hands page routes to React Router and\n * returns 404 for framework / asset paths that React Router doesn't own.\n */\nexport function createH3SSRHandler(getBuild: () => Promise<unknown> | unknown) {\n const handler = createRequestHandler(getBuild as any);\n return defineEventHandler(async (event) => {\n const basePath = getAppBasePath();\n const p = stripAppBasePath(event.url.pathname);\n if (isFrameworkOrAssetPath(p)) {\n return new Response(null, { status: 404 });\n }\n try {\n const request = requestWithPathname(event.req as Request, p, basePath);\n // SSR renders an IMPERSONAL public shell — we deliberately do NOT read the\n // request's session/cookies here, and pin an explicitly anonymous request\n // context. That keeps the SSR HTML/.data identical for every visitor so it\n // can be hard-cached at the CDN for everyone (see applyDefaultSsrCacheHeader).\n //\n // Consequence: SSR loaders that call `getRequestUserEmail()` / `accessFilter()`\n // always see the unauthenticated branch and render public content only. Any\n // per-user view (private records, share-grant access, who's logged in) MUST\n // be resolved CLIENT-SIDE after load, never baked into SSR. Do not re-pin the\n // session here to \"fix\" a per-user page — that silently makes the page\n // uncacheable and/or leaks one user's data into another's cached copy.\n const ctx = { userEmail: undefined, orgId: undefined };\n if (request.method === \"HEAD\") {\n const getRequest = new Request(request.url, {\n method: \"GET\",\n headers: request.headers,\n signal: request.signal,\n });\n const response = await runWithRequestContext(ctx, () =>\n handler(getRequest),\n );\n return await rewriteMountedResponse(\n new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n basePath,\n p,\n request.url,\n );\n }\n return await rewriteMountedResponse(\n await runWithRequestContext(ctx, () => handler(request)),\n basePath,\n p,\n request.url,\n );\n } catch (err) {\n // Log the full stack server-side, but never leak it to the client.\n // Stack traces expose file paths, library versions, and code structure\n // that aid reconnaissance attacks. In dev we surface the message text\n // so devtools shows something useful; in prod we return a bare 500.\n console.error(\"[ssr-handler] SSR error:\", err);\n const isProd = process.env.NODE_ENV === \"production\";\n const body = isProd\n ? \"Internal Server Error\"\n : `Internal Server Error: ${(err as Error)?.message ?? err}`;\n return new Response(body, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ssr-handler.js","sourceRoot":"","sources":["../../src/server/ssr-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineEventHandler } from \"h3\";\n/**\n * Shared SSR catch-all handler for React Router framework mode.\n *\n * Templates wire this up via:\n *\n * // server/routes/[...page].get.ts\n * import { createH3SSRHandler } from \"@agent-native/core/server/ssr-handler\";\n * export default createH3SSRHandler(\n * () => import(\"virtual:react-router/server-build\"),\n * );\n *\n * The `getBuild` callback MUST live in the template's own source so Vite's\n * @react-router/dev plugin can resolve the `virtual:` module. Pulling the\n * import into core (e.g. via a re-export) puts it in node_modules where\n * Vite's SSR externalizer leaves it untouched and Node's ESM loader rejects\n * the unknown scheme — silently 302'ing every request to \"/\".\n */\nimport { createRequestHandler } from \"react-router\";\n\nimport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_PATH,\n} from \"../shared/cache-control.js\";\nimport {\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n withAgentNativeSocialImageCacheBuster,\n} from \"../shared/social-meta.js\";\nimport {\n GA_CSP_CONNECT_HOSTS,\n GA_CSP_IMG_HOSTS,\n GA_CSP_SCRIPT_HOSTS,\n getGaInlineConfigScriptBody,\n} from \"./analytics.js\";\nimport {\n getAppBasePathFromViteEnv,\n stripAppBasePath as canonicalStripAppBasePath,\n} from \"./app-base-path.js\";\nimport { runWithRequestContext } from \"./request-context.js\";\nimport { computeInlineScriptHash } from \"./security-headers.js\";\nimport { getSentryClientConfigScript } from \"./sentry-config.js\";\n\nexport {\n DEFAULT_SSR_CACHE_HEADERS,\n DEFAULT_SPECULATION_RULES_HEADER,\n DEFAULT_SSR_CACHE_CONTROL,\n} from \"../shared/cache-control.js\";\n\nfunction getAppBasePath(): string {\n return getAppBasePathFromViteEnv();\n}\n\nfunction stripAppBasePath(pathname: string): string {\n return canonicalStripAppBasePath(pathname, getAppBasePath());\n}\n\nfunction stripBasePath(pathname: string, basePath: string): string {\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\nfunction requestWithPathname(\n request: Request,\n pathname: string,\n basePath: string,\n): Request {\n const url = new URL(request.url);\n let changed = false;\n if (basePath && pathname === \"/__manifest\") {\n const paths = url.searchParams.get(\"paths\");\n if (paths) {\n const strippedPaths = paths\n .split(\",\")\n .map((path) => stripBasePath(path, basePath))\n .join(\",\");\n if (strippedPaths !== paths) {\n url.searchParams.set(\"paths\", strippedPaths);\n changed = true;\n }\n }\n }\n if (url.pathname !== pathname) {\n url.pathname = pathname;\n changed = true;\n }\n if (!changed) return request;\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers: request.headers,\n signal: request.signal,\n };\n if (request.body && ![\"GET\", \"HEAD\"].includes(request.method.toUpperCase())) {\n init.body = request.body;\n init.duplex = \"half\";\n }\n return new Request(url, init);\n}\n\nfunction prefixMountedPath(path: string, basePath: string): string {\n if (!basePath || !path.startsWith(\"/\") || path.startsWith(\"//\")) return path;\n if (path === basePath || path.startsWith(`${basePath}/`)) return path;\n return `${basePath}${path}`;\n}\n\nfunction prefixMountedHtml(html: string, basePath: string): string {\n if (!basePath) return html;\n return html\n .replace(\n /\\b(href|src|action|formaction|poster)=([\"'])(\\/(?!\\/)[^\"']*)\\2/g,\n (_match, attr: string, quote: string, path: string) =>\n `${attr}=${quote}${prefixMountedPath(path, basePath)}${quote}`,\n )\n .replace(/url\\(([\"']?)(\\/(?!\\/)[^)'\" ]+)\\1\\)/g, (_match, quote, path) => {\n const q = quote || \"\";\n return `url(${q}${prefixMountedPath(path, basePath)}${q})`;\n });\n}\n\nfunction injectHeadScript(html: string, script: string | null): string {\n if (!script) return html;\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n return html.slice(0, headCloseIdx) + script + html.slice(headCloseIdx);\n}\n\nconst OG_IMAGE_META_RE = /<meta\\b(?=[^>]*\\bproperty=([\"'])og:image\\1)[^>]*>/i;\nconst TWITTER_CARD_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:card\\1)[^>]*>/i;\nconst TWITTER_IMAGE_META_RE =\n /<meta\\b(?=[^>]*\\bname=([\"'])twitter:image\\1)[^>]*>/i;\n\nfunction defaultSocialImageUrl(requestUrl: string, basePath: string): string {\n return withAgentNativeSocialImageCacheBuster(\n new URL(\n prefixMountedPath(AGENT_NATIVE_SOCIAL_IMAGE_PATH, basePath),\n requestUrl,\n ).toString(),\n );\n}\n\nfunction injectDefaultSocialImageMeta(html: string, imageUrl: string): string {\n const headCloseIdx = html.indexOf(\"</head>\");\n if (headCloseIdx === -1) return html;\n\n const hasAnySocialImage =\n OG_IMAGE_META_RE.test(html) || TWITTER_IMAGE_META_RE.test(html);\n const tags: string[] = [];\n\n if (!hasAnySocialImage) {\n tags.push(`<meta property=\"og:image\" content=\"${imageUrl}\">`);\n tags.push(`<meta property=\"og:image:secure_url\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta property=\"og:image:type\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_TYPE}\">`,\n );\n tags.push(\n `<meta property=\"og:image:width\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_WIDTH}\">`,\n );\n tags.push(\n `<meta property=\"og:image:height\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT}\">`,\n );\n tags.push(\n `<meta property=\"og:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n if (!TWITTER_CARD_META_RE.test(html)) {\n tags.push(`<meta name=\"twitter:card\" content=\"summary_large_image\">`);\n }\n if (!hasAnySocialImage) {\n tags.push(`<meta name=\"twitter:image\" content=\"${imageUrl}\">`);\n tags.push(\n `<meta name=\"twitter:image:alt\" content=\"${AGENT_NATIVE_SOCIAL_IMAGE_ALT}\">`,\n );\n }\n\n if (tags.length === 0) return html;\n return html.slice(0, headCloseIdx) + tags.join(\"\") + html.slice(headCloseIdx);\n}\n\nfunction isSsrHtmlOrDataResponse(\n headers: Headers,\n status: number,\n pathname: string,\n): boolean {\n if (status < 200 || status >= 400) return false;\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (contentType.includes(\"text/html\")) return true;\n return pathname.endsWith(\".data\") && contentType.includes(\"text/x-script\");\n}\n\n/**\n * Apply the SSR cache policy to the response headers.\n *\n * ┌──────────────────────────────────────────────────────────────────────────┐\n * │ SSR IS A PUBLIC, HARD-CDN-CACHED SHELL — SERVED IDENTICALLY TO EVERYONE. │\n * │ │\n * │ Every SSR HTML / React Router `.data` response gets the same public │\n * │ stale-while-revalidate policy for ALL visitors, authenticated or not, so │\n * │ the edge serves one shared copy and never stampedes origin. │\n * │ │\n * │ DO NOT reintroduce per-user / cookie-based cache variation here (no │\n * │ `private`, no `no-store`, no `Vary: Cookie`, no \"authenticated → don't │\n * │ cache\" branch). That makes pages uncacheable for every logged-in visitor, │\n * │ which is slow and expensive — exactly the regression this guardrail │\n * │ prevents. The reason it is SAFE to hard-cache is that the SSR response is │\n * │ impersonal: `createH3SSRHandler` renders without reading the request's │\n * │ session/cookies, so there is no per-user data baked into the HTML. ALL │\n * │ per-user state (who's logged in, private records, access checks) is │\n * │ resolved CLIENT-SIDE after load. Keep it that way: if you need the SSR │\n * │ output to differ per user, the fix is to move that work client-side, not │\n * │ to disable caching here. │\n * └──────────────────────────────────────────────────────────────────────────┘\n */\nfunction applyDefaultSsrCacheHeader(\n headers: Headers,\n status: number,\n pathname: string,\n) {\n if (!isSsrHtmlOrDataResponse(headers, status, pathname)) return;\n\n // Netlify Functions/proxies are not cached by default. Set all three cache\n // headers: Cache-Control for browsers, CDN-Cache-Control for generic CDNs,\n // and Netlify-CDN-Cache-Control (with durable) so Netlify's shared cache\n // actually serves SSR HTML/.data from the edge instead of forwarding every\n // request to origin — for every visitor, authenticated or not.\n for (const [name, value] of Object.entries(DEFAULT_SSR_CACHE_HEADERS)) {\n headers.set(name, value);\n }\n}\n\nfunction applyDefaultSpeculationRulesHeader(\n headers: Headers,\n status: number,\n basePath: string,\n) {\n if (status < 200 || status >= 400) return;\n if (headers.has(\"speculation-rules\")) return;\n\n const contentType = headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n if (!contentType.includes(\"text/html\")) return;\n\n // Cloudflare Speed Brain injects its own Speculation-Rules header when the\n // origin omits one. Those browser prefetches carry `Sec-Purpose: prefetch`,\n // and Cloudflare refuses cache-ineligible dynamic pages with a 503 before\n // the request can reach Netlify/origin. We publish an explicit no-op ruleset\n // by default so Cloudflare does not inject its edge prefetch rules. Preserve\n // an app-provided Speculation-Rules header above if a template deliberately\n // owns this behavior.\n const rulesPath = prefixMountedPath(DEFAULT_SPECULATION_RULES_PATH, basePath);\n headers.set(\"speculation-rules\", `\"${rulesPath}\"`);\n}\n\n/**\n * Extract the plain JS body from a `<script ...>body</script>` string.\n * Returns `null` if the input is falsy or has no recognisable `</script>` end.\n * Used to compute the sha256 hash of framework-injected inline scripts so the\n * hash can be listed in app-owned `script-src` CSP directives.\n */\nfunction extractScriptBody(scriptTag: string | null): string | null {\n if (!scriptTag) return null;\n const start = scriptTag.indexOf(\">\") + 1;\n const end = scriptTag.lastIndexOf(\"</script>\");\n if (start <= 0 || end < start) return null;\n return scriptTag.slice(start, end);\n}\n\ntype CspDirective = {\n name: string;\n tokens: string[];\n};\n\nconst CSP_DIRECTIVES_WITH_VALUE_TOKENS = new Set([\n \"base-uri\",\n \"block-all-mixed-content\",\n \"child-src\",\n \"connect-src\",\n \"default-src\",\n \"fenced-frame-src\",\n \"font-src\",\n \"form-action\",\n \"frame-ancestors\",\n \"frame-src\",\n \"img-src\",\n \"manifest-src\",\n \"media-src\",\n \"navigate-to\",\n \"object-src\",\n \"plugin-types\",\n \"prefetch-src\",\n \"referrer\",\n \"reflected-xss\",\n \"require-sri-for\",\n \"require-trusted-types-for\",\n \"report-to\",\n \"report-uri\",\n \"sandbox\",\n \"script-src\",\n \"script-src-attr\",\n \"script-src-elem\",\n \"style-src\",\n \"style-src-attr\",\n \"style-src-elem\",\n \"trusted-types\",\n \"upgrade-insecure-requests\",\n \"webrtc\",\n \"worker-src\",\n]);\n\nfunction hasCommaJoinedCspPolicies(policy: string): boolean {\n let commaIndex = policy.indexOf(\",\");\n while (commaIndex !== -1) {\n const afterComma = policy.slice(commaIndex + 1);\n const directive = /^\\s+([a-z][a-z0-9-]*)(?=\\s|;|$)/i.exec(afterComma)?.[1];\n if (\n directive &&\n CSP_DIRECTIVES_WITH_VALUE_TOKENS.has(directive.toLowerCase())\n ) {\n return true;\n }\n commaIndex = policy.indexOf(\",\", commaIndex + 1);\n }\n return false;\n}\n\nfunction parseCsp(policy: string): CspDirective[] {\n return policy\n .split(\";\")\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => {\n const [name = \"\", ...tokens] = part.split(/\\s+/);\n return { name: name.toLowerCase(), tokens };\n })\n .filter((directive) => directive.name);\n}\n\nfunction serializeCsp(directives: CspDirective[]): string {\n return directives\n .map((directive) =>\n [directive.name, ...directive.tokens].filter(Boolean).join(\" \"),\n )\n .join(\"; \");\n}\n\nfunction appendCspTokens(\n tokens: string[],\n additions: readonly string[],\n): string[] {\n if (!additions.length) return tokens;\n const next = tokens.filter((token) => token !== \"'none'\");\n const seen = new Set(next);\n for (const token of additions) {\n if (!token || seen.has(token)) continue;\n next.push(token);\n seen.add(token);\n }\n return next;\n}\n\nfunction findCspDirective(\n directives: CspDirective[],\n name: string,\n): CspDirective | undefined {\n return directives.find((directive) => directive.name === name);\n}\n\nfunction appendToExistingOrDefaultCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n if (!additions.length) return;\n const existing = findCspDirective(directives, name);\n if (existing) {\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc) return;\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n}\n\nfunction appendToExistingCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): void {\n const existing = findCspDirective(directives, name);\n if (!existing) return;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n}\n\nfunction ensureCspDirective(\n directives: CspDirective[],\n name: string,\n tokens: readonly string[],\n): void {\n if (findCspDirective(directives, name)) return;\n directives.push({ name, tokens: [...tokens] });\n}\n\nfunction hasStrictNonceScriptPolicy(tokens: readonly string[]): boolean {\n return tokens.some(\n (token) => token === \"'strict-dynamic'\" || token.startsWith(\"'nonce-\"),\n );\n}\n\nfunction appendToScriptCspDirective(\n directives: CspDirective[],\n name: string,\n additions: readonly string[],\n): boolean {\n const existing = findCspDirective(directives, name);\n if (existing) {\n if (hasStrictNonceScriptPolicy(existing.tokens)) return false;\n existing.tokens = appendCspTokens(existing.tokens, additions);\n return true;\n }\n\n const defaultSrc = findCspDirective(directives, \"default-src\");\n if (!defaultSrc || hasStrictNonceScriptPolicy(defaultSrc.tokens)) {\n return false;\n }\n directives.push({\n name,\n tokens: appendCspTokens([...defaultSrc.tokens], additions),\n });\n return true;\n}\n\nfunction appendToEffectiveScriptElementCspDirective(\n directives: CspDirective[],\n additions: readonly string[],\n): boolean {\n const scriptSrcElem = findCspDirective(directives, \"script-src-elem\");\n if (scriptSrcElem) {\n if (hasStrictNonceScriptPolicy(scriptSrcElem.tokens)) return false;\n scriptSrcElem.tokens = appendCspTokens(scriptSrcElem.tokens, additions);\n return true;\n }\n\n return appendToScriptCspDirective(directives, \"script-src\", additions);\n}\n\nfunction augmentExistingEnforcedCspForFrameworkScripts(\n policy: string,\n options: {\n gaScriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n // Multiple CSP headers are surfaced by Headers.get() as one comma-joined\n // string. CSP is not a comma-list header, so serializing a parsed combined\n // value would turn two policies into one invalid policy. Leave those headers\n // app-owned; a comma inside a source/report URL is still safe to parse.\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n if (options.gaEnabled) {\n const addedScriptElement = appendToEffectiveScriptElementCspDirective(\n directives,\n options.gaScriptSrcTokens,\n );\n if (addedScriptElement) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n }\n\n ensureCspDirective(directives, \"object-src\", [\"'none'\"]);\n ensureCspDirective(directives, \"base-uri\", [\"'self'\"]);\n\n return serializeCsp(directives);\n}\n\nfunction augmentExistingReportOnlyCspForFrameworkScripts(\n policy: string,\n options: {\n scriptSrcTokens: readonly string[];\n gaEnabled: boolean;\n },\n): string {\n if (hasCommaJoinedCspPolicies(policy)) return policy;\n\n const directives = parseCsp(policy);\n if (!directives.length) return policy;\n\n appendToExistingOrDefaultCspDirective(\n directives,\n \"script-src\",\n options.scriptSrcTokens,\n );\n appendToExistingCspDirective(\n directives,\n \"script-src-elem\",\n options.scriptSrcTokens,\n );\n\n if (options.gaEnabled) {\n appendToExistingOrDefaultCspDirective(\n directives,\n \"connect-src\",\n GA_CSP_CONNECT_HOSTS,\n );\n appendToExistingOrDefaultCspDirective(\n directives,\n \"img-src\",\n GA_CSP_IMG_HOSTS,\n );\n }\n\n return serializeCsp(directives);\n}\n\n/**\n * Apply a Content-Security-Policy header to HTML document responses.\n *\n * Two directives are always enforced in production:\n *\n * - `object-src 'none'` — disables Flash / Java / PDF plugin execution,\n * which are a reliable code-execution vector even in modern browsers.\n * - `base-uri 'self'` — prevents a `<base href=\"...\">` injection from\n * hijacking all relative URLs in the document (a common attack target when\n * user-controlled content reaches the HTML).\n *\n * A third directive, `script-src`, is emitted via `Content-Security-Policy-\n * Report-Only` rather than enforced when the app has no existing document CSP.\n * The framework injects inline scripts for analytics, Sentry, and template\n * setup, and hosted apps need Google Tag Manager to load without noisy CSP\n * diagnostics. The report-only policy is intentionally permissive for scripts:\n * it includes `'unsafe-inline'` plus the known GA/GTM loader hosts.\n *\n * If an app or host already sends an enforced CSP with `script-src`,\n * `script-src-elem`, `connect-src`, `img-src`, or `default-src`, we merge only\n * GA-specific allowances into existing host/hash policies. Strict nonce or\n * `strict-dynamic` script policies stay app-owned because blindly appending\n * hashes or hosts would widen the policy without reliably loading our injected\n * scripts.\n *\n * Templates additionally render a theme-init inline script whose exact content\n * varies by template (default theme param, custom docs variant, etc.) and which\n * is rendered by React Router, not this handler, so its hash is not available\n * here. Shipping script-src as Report-Only surfaces the remaining violations\n * without breaking template customisations; teams can graduate to enforcement\n * once their hashes are enumerated.\n *\n * Skipped in development (`NODE_ENV !== 'production'`) so HMR eval and Vite\n * dev-server injects are never blocked. Set `AGENT_NATIVE_DISABLE_DOC_CSP=1`\n * to opt out in production for a template with exotic needs.\n */\nfunction applyDocumentCsp(headers: Headers, sentryScript: string | null): void {\n if (process.env.NODE_ENV !== \"production\") return;\n if (process.env.AGENT_NATIVE_DISABLE_DOC_CSP === \"1\") return;\n\n // script-src as Report-Only: keep this deliberately loose so the framework's\n // injected analytics and template bootstrap scripts do not look blocked in\n // browser diagnostics.\n const sentryBody = extractScriptBody(sentryScript);\n const sentryHash = sentryBody ? computeInlineScriptHash(sentryBody) : null;\n const gaInlineBody = getGaInlineConfigScriptBody();\n const gaHash = gaInlineBody ? computeInlineScriptHash(gaInlineBody) : null;\n const gaHosts = gaInlineBody ? [...GA_CSP_SCRIPT_HOSTS] : [];\n const gaScriptSrcTokens = [\n \"'unsafe-inline'\",\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n const scriptSrcTokens = [\n \"'self'\",\n \"'unsafe-inline'\",\n ...(sentryHash ? [sentryHash] : []),\n ...(gaHash ? [gaHash] : []),\n ...gaHosts,\n ];\n\n const cspAugmentOptions = {\n scriptSrcTokens,\n gaScriptSrcTokens,\n gaEnabled: Boolean(gaInlineBody),\n };\n const existing = headers.get(\"content-security-policy\") ?? \"\";\n if (!existing) {\n headers.set(\n \"content-security-policy\",\n \"object-src 'none'; base-uri 'self'\",\n );\n } else {\n headers.set(\n \"content-security-policy\",\n augmentExistingEnforcedCspForFrameworkScripts(\n existing,\n cspAugmentOptions,\n ),\n );\n }\n\n const scriptSrc = `script-src ${scriptSrcTokens.join(\" \")}`;\n const existingRo = headers.get(\"content-security-policy-report-only\") ?? \"\";\n if (!existingRo) {\n headers.set(\"content-security-policy-report-only\", scriptSrc);\n } else {\n headers.set(\n \"content-security-policy-report-only\",\n augmentExistingReportOnlyCspForFrameworkScripts(\n existingRo,\n cspAugmentOptions,\n ),\n );\n }\n}\n\nfunction isFrameworkOrAssetPath(pathname: string): boolean {\n return (\n pathname.startsWith(\"/.well-known/\") ||\n pathname.startsWith(\"/_agent_native/\") ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname.startsWith(\"/api/\") ||\n pathname.startsWith(\"/@vite/\") ||\n pathname.startsWith(\"/@id/\") ||\n pathname.startsWith(\"/@fs/\") ||\n pathname === \"/@react-refresh\" ||\n pathname === \"/__vite_ping\" ||\n pathname === \"/__open-in-editor\" ||\n pathname === \"/favicon.ico\" ||\n pathname === \"/favicon.png\" ||\n (/\\.\\w+$/.test(pathname) && !pathname.endsWith(\".data\"))\n );\n}\n\nasync function rewriteMountedResponse(\n response: Response,\n basePath: string,\n pathname: string,\n requestUrl: string,\n): Promise<Response> {\n const sentryClientConfigScript = getSentryClientConfigScript();\n const headers = new Headers(response.headers);\n applyDefaultSsrCacheHeader(headers, response.status, pathname);\n applyDefaultSpeculationRulesHeader(headers, response.status, basePath);\n\n const location = headers.get(\"location\");\n if (location?.startsWith(\"/\") && !location.startsWith(\"//\")) {\n headers.set(\"location\", prefixMountedPath(location, basePath));\n }\n\n const contentType = headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"text/html\") || !response.body) {\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n }\n\n const html = await response.text();\n headers.delete(\"content-length\");\n applyDocumentCsp(headers, sentryClientConfigScript);\n return new Response(\n injectHeadScript(\n injectDefaultSocialImageMeta(\n prefixMountedHtml(html, basePath),\n defaultSocialImageUrl(requestUrl, basePath),\n ),\n sentryClientConfigScript,\n ),\n {\n status: response.status,\n statusText: response.statusText,\n headers,\n },\n );\n}\n\n/**\n * Create an h3 catch-all that hands page routes to React Router and\n * returns 404 for framework / asset paths that React Router doesn't own.\n */\nexport function createH3SSRHandler(getBuild: () => Promise<unknown> | unknown) {\n const handler = createRequestHandler(getBuild as any);\n return defineEventHandler(async (event) => {\n const basePath = getAppBasePath();\n const p = stripAppBasePath(event.url.pathname);\n if (isFrameworkOrAssetPath(p)) {\n return new Response(null, { status: 404 });\n }\n try {\n const request = requestWithPathname(event.req as Request, p, basePath);\n // SSR renders an IMPERSONAL public shell — we deliberately do NOT read the\n // request's session/cookies here, and pin an explicitly anonymous request\n // context. That keeps the SSR HTML/.data identical for every visitor so it\n // can be hard-cached at the CDN for everyone (see applyDefaultSsrCacheHeader).\n //\n // Consequence: SSR loaders that call `getRequestUserEmail()` / `accessFilter()`\n // always see the unauthenticated branch and render public content only. Any\n // per-user view (private records, share-grant access, who's logged in) MUST\n // be resolved CLIENT-SIDE after load, never baked into SSR. Do not re-pin the\n // session here to \"fix\" a per-user page — that silently makes the page\n // uncacheable and/or leaks one user's data into another's cached copy.\n const ctx = { userEmail: undefined, orgId: undefined };\n if (request.method === \"HEAD\") {\n const getRequest = new Request(request.url, {\n method: \"GET\",\n headers: request.headers,\n signal: request.signal,\n });\n const response = await runWithRequestContext(ctx, () =>\n handler(getRequest),\n );\n return await rewriteMountedResponse(\n new Response(null, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n basePath,\n p,\n request.url,\n );\n }\n return await rewriteMountedResponse(\n await runWithRequestContext(ctx, () => handler(request)),\n basePath,\n p,\n request.url,\n );\n } catch (err) {\n // Log the full stack server-side, but never leak it to the client.\n // Stack traces expose file paths, library versions, and code structure\n // that aid reconnaissance attacks. In dev we surface the message text\n // so devtools shows something useful; in prod we return a bare 500.\n console.error(\"[ssr-handler] SSR error:\", err);\n const isProd = process.env.NODE_ENV === \"production\";\n const body = isProd\n ? \"Internal Server Error\"\n : `Internal Server Error: ${(err as Error)?.message ?? err}`;\n return new Response(body, {\n status: 500,\n headers: { \"content-type\": \"text/plain\" },\n });\n }\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAMzD,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxD,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;IACjB,YAAY,OAAO,SAAc,EAGhC;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,6BAA6B,GAAG,SAAS,EAC9C,GAAG,EAAE,aAAa,GACjB,aAAa,CAEf;AAWD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,MAAM,GAAE,aAA+B,EACvC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,CA0DL;AAgED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,EAAE,GAAG,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAMzD,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxD,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;IACjB,YAAY,OAAO,SAAc,EAGhC;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,6BAA6B,GAAG,SAAS,EAC9C,GAAG,EAAE,aAAa,GACjB,aAAa,CAEf;AAWD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,MAAM,GAAE,aAA+B,EACvC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,CA0DL;AAgED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,EAAE,GAAG,CAAC;CACf;AAiGD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,aAA+B,GACtC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmChC;AA+CD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,SAAS,GAAG,OAAkB,EACvC,GAAG,GAAE,aAA+B,GACnC,OAAO,CAAC,cAAc,CAAC,CAWzB"}
|
package/dist/sharing/access.js
CHANGED
|
@@ -173,6 +173,59 @@ function higherShareRole(a, b) {
|
|
|
173
173
|
return a;
|
|
174
174
|
return ROLE_RANK[b] > ROLE_RANK[a] ? b : a;
|
|
175
175
|
}
|
|
176
|
+
function columnName(column) {
|
|
177
|
+
const candidate = column;
|
|
178
|
+
const name = candidate?.name ?? candidate?.config?.name ?? candidate?._?.name;
|
|
179
|
+
return typeof name === "string" && name ? name : null;
|
|
180
|
+
}
|
|
181
|
+
function missingColumnName(err) {
|
|
182
|
+
const error = err;
|
|
183
|
+
const message = error?.message ?? "";
|
|
184
|
+
if (error?.code !== "42703" &&
|
|
185
|
+
!/no such column|does not exist/i.test(message)) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const quoted = message.match(/column\s+"([^"]+)"\s+does not exist/i)?.[1];
|
|
189
|
+
if (quoted)
|
|
190
|
+
return quoted;
|
|
191
|
+
const sqlite = message.match(/no such column:\s+["`]?([\w.]+)["`]?/i)?.[1];
|
|
192
|
+
if (sqlite)
|
|
193
|
+
return sqlite.split(".").pop() ?? sqlite;
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
function selectAllExistingResourceColumns(resourceTable, omittedColumnNames) {
|
|
197
|
+
const selection = {};
|
|
198
|
+
for (const [key, column] of Object.entries(resourceTable)) {
|
|
199
|
+
const name = columnName(column);
|
|
200
|
+
if (!name || omittedColumnNames.has(name))
|
|
201
|
+
continue;
|
|
202
|
+
selection[key] = column;
|
|
203
|
+
}
|
|
204
|
+
return selection;
|
|
205
|
+
}
|
|
206
|
+
async function loadResourceForAccess(reg, resourceId) {
|
|
207
|
+
const db = reg.getDb();
|
|
208
|
+
const omittedColumnNames = new Set();
|
|
209
|
+
for (let attempt = 0; attempt < 12; attempt++) {
|
|
210
|
+
try {
|
|
211
|
+
const query = omittedColumnNames.size === 0
|
|
212
|
+
? db.select()
|
|
213
|
+
: db.select(selectAllExistingResourceColumns(reg.resourceTable, omittedColumnNames));
|
|
214
|
+
const [resource] = await query
|
|
215
|
+
.from(reg.resourceTable)
|
|
216
|
+
.where(eq(reg.resourceTable.id, resourceId));
|
|
217
|
+
return resource ?? null;
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
const missing = missingColumnName(err);
|
|
221
|
+
if (!missing || omittedColumnNames.has(missing))
|
|
222
|
+
throw err;
|
|
223
|
+
omittedColumnNames.add(missing);
|
|
224
|
+
console.warn(`[sharing] ${reg.type} access lookup omitted missing column ${missing}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new Error(`Could not load ${reg.type} ${resourceId}: too many missing resource columns`);
|
|
228
|
+
}
|
|
176
229
|
/**
|
|
177
230
|
* Return the effective role the current user has on a specific resource, or
|
|
178
231
|
* null if they have no access. Loads the resource and relevant share rows.
|
|
@@ -180,11 +233,7 @@ function higherShareRole(a, b) {
|
|
|
180
233
|
export async function resolveAccess(resourceType, resourceId, rawCtx = currentAccess()) {
|
|
181
234
|
const reg = requireShareableResource(resourceType);
|
|
182
235
|
const ctx = resolveRegisteredAccessContext(reg, rawCtx);
|
|
183
|
-
const
|
|
184
|
-
const [resource] = await db
|
|
185
|
-
.select()
|
|
186
|
-
.from(reg.resourceTable)
|
|
187
|
-
.where(eq(reg.resourceTable.id, resourceId));
|
|
236
|
+
const resource = await loadResourceForAccess(reg, resourceId);
|
|
188
237
|
if (!resource)
|
|
189
238
|
return null;
|
|
190
239
|
const { userEmail, orgId } = ctx;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\n\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\nexport function resolveRegisteredAccessContext(\n reg: ShareableResourceRegistration | undefined,\n ctx: AccessContext,\n): AccessContext {\n return reg?.resolveAccessContext ? reg.resolveAccessContext(ctx) : ctx;\n}\n\nfunction normalizeEmailForAccess(email: string | undefined): string | null {\n const normalized = email?.trim().toLowerCase();\n return normalized || null;\n}\n\nfunction emailColumnMatches(column: any, email: string): SQL {\n return sql`lower(${column}) = ${email}`;\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n rawCtx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (normalizedUserEmail) {\n clauses.push(\n and(\n emailColumnMatches(resourceTable.ownerEmail, normalizedUserEmail),\n ownerScopeFilter(reg, resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (normalizedUserEmail) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and lower(${sharesTable.principalId}) = ${normalizedUserEmail}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n if (reg?.ownerAccessIgnoresOrg === true) return sql`1=1`;\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(\n reg: ShareableResourceRegistration | undefined,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg?.ownerAccessIgnoresOrg === true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nfunction restrictedShareScopeSql(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n // Restricted resources (extensions) must stay inside their resource org even\n // if stale cross-org share rows already exist from older code or bad data.\n if (reg?.requireOrgMemberForUserShares !== true) return sql`1=1`;\n if (!ctx.orgId) return sql`1=0`;\n return eq(resourceTable.orgId, ctx.orgId);\n}\n\nfunction explicitSharesAllowedForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg.requireOrgMemberForUserShares !== true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\nasync function publicAccessRoleForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): Promise<ShareRole> {\n const roleResolver = reg.publicAccessRole;\n if (!roleResolver) return \"viewer\";\n return typeof roleResolver === \"function\"\n ? await roleResolver(resource, ctx)\n : roleResolver;\n}\n\nfunction higherShareRole(a: ShareRole, b: ShareRole | null): ShareRole {\n if (!b) return a;\n return ROLE_RANK[b] > ROLE_RANK[a] ? b : a;\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n const db = reg.getDb() as any;\n\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n\n if (\n normalizedUserEmail &&\n normalizeEmailForAccess(resource.ownerEmail) === normalizedUserEmail &&\n ownerMatchesActiveScope(reg, resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless the resource registration\n // deliberately grants a stronger public-by-link role or explicit shares\n // upgrade the current principal.\n const publicRole = await publicAccessRoleForResource(reg, resource, ctx);\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: higherShareRole(publicRole, role), resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n resource: any,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n if (!normalizedUserEmail && !orgId) return null;\n if (!explicitSharesAllowedForResource(reg, resource, ctx)) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (normalizedUserEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n emailColumnMatches(reg.sharesTable.principalId, normalizedUserEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAY,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,mBAAmB,EACnB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GAEzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAkB,MAAM,aAAa,CAAC;AAExD;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAC9B,aAAkB;IAElB,KAAK,MAAM,GAAG,IAAI,sBAAsB,EAAE,EAAE,CAAC;QAC3C,IAAI,GAAG,CAAC,aAAa,KAAK,aAAa;YAAE,OAAO,GAAG,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,UAAU,GAAG,GAAG,CAAC;IACjB,YAAY,OAAO,GAAG,WAAW;QAC/B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAOD,wEAAwE;AACxE,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,SAAS,EAAE,mBAAmB,EAAE;QAChC,KAAK,EAAE,eAAe,EAAE;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,GAA8C,EAC9C,GAAkB;IAElB,OAAO,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACzE,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAyB;IACxD,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,OAAO,UAAU,IAAI,IAAI,CAAC;AAC5B,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAW,EAAE,KAAa;IACpD,OAAO,GAAG,CAAA,SAAS,MAAM,OAAO,KAAK,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAC1B,aAAkB,EAClB,WAAgB,EAChB,MAAM,GAAkB,aAAa,EAAE,EACvC,OAAO,GAAc,QAAQ,EAC7B,OAAO,GAAgC,EAAE;IAEzC,yEAAyE;IACzE,uEAAuE;IACvE,0EAA0E;IAC1E,aAAa;IACb,MAAM,GAAG,GAAG,uBAAuB,CAAC,aAAa,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,8BAA8B,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxD,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IACjC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,EAAE,WAAW,KAAK,KAAK,CAAC;IACjD,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,KAAK,CAAC,IAAI,aAAa,CAAC;IACxE,MAAM,OAAO,GAAU,EAAE,CAAC;IAE1B,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CACV,GAAG,CACD,kBAAkB,CAAC,aAAa,CAAC,UAAU,EAAE,mBAAmB,CAAC,EACjE,gBAAgB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,CAAC,CACzC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CACV,GAAG,CACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,EACnC,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAC9B,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CACV,GAAG,CAAA,yBAAyB,WAAW;0BACnB,WAAW,CAAC,UAAU,MAAM,aAAa,CAAC,EAAE;0BAC5C,WAAW,CAAC,aAAa;gCACnB,WAAW,CAAC,WAAW,OAAO,mBAAmB;0BACvD,UAAU;0BACV,UAAU,CAAC,OAAO,CAAC,GAAG,CAC3C,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CACV,GAAG,CAAA,yBAAyB,WAAW;0BACnB,WAAW,CAAC,UAAU,MAAM,aAAa,CAAC,EAAE;0BAC5C,WAAW,CAAC,aAAa;0BACzB,WAAW,CAAC,WAAW,MAAM,KAAK;0BAClC,UAAU;0BACV,UAAU,CAAC,OAAO,CAAC,GAAG,CAC3C,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,CAAA,KAAK,CAAC;AACpC,CAAC;AAED,SAAS,gBAAgB,CACvB,GAA8C,EAC9C,aAAkB,EAClB,GAAkB;IAElB,IAAI,GAAG,EAAE,qBAAqB,KAAK,IAAI;QAAE,OAAO,GAAG,CAAA,KAAK,CAAC;IACzD,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,yEAAyE;QACzE,2EAA2E;QAC3E,uEAAuE;QACvE,OAAO,EAAE,CACP,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,EAClC,GAAG,CAAA,GAAG,aAAa,CAAC,KAAK,UAAU,CACnC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAA,GAAG,aAAa,CAAC,KAAK,UAAU,CAAC;AAC7C,CAAC;AAED,SAAS,uBAAuB,CAC9B,GAA8C,EAC9C,QAAa,EACb,GAAkB;IAElB,IAAI,GAAG,EAAE,qBAAqB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC;IAC9C,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,GAAG,CAAC,KAAK,KAAK,aAAa,CAAC;AACrC,CAAC;AAED,SAAS,UAAU,CAAC,OAAkB;IACpC,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,4BAA4B;QAC5B,OAAO,GAAG,CAAA,KAAK,CAAC;IAClB,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,GAAG,CAAA,4BAA4B,CAAC;IACzC,CAAC;IACD,OAAO,GAAG,CAAA,gBAAgB,CAAC;AAC7B,CAAC;AAED,SAAS,uBAAuB,CAC9B,GAA8C,EAC9C,aAAkB,EAClB,GAAkB;IAElB,6EAA6E;IAC7E,2EAA2E;IAC3E,IAAI,GAAG,EAAE,6BAA6B,KAAK,IAAI;QAAE,OAAO,GAAG,CAAA,KAAK,CAAC;IACjE,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,GAAG,CAAA,KAAK,CAAC;IAChC,OAAO,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,gCAAgC,CACvC,GAAkC,EAClC,QAAa,EACb,GAAkB;IAElB,IAAI,GAAG,CAAC,6BAA6B,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC;IAC9C,OAAO,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,aAAa,KAAK,GAAG,CAAC,KAAK,CAAC;AACvE,CAAC;AASD,KAAK,UAAU,2BAA2B,CACxC,GAAkC,EAClC,QAAa,EACb,GAAkB;IAElB,MAAM,YAAY,GAAG,GAAG,CAAC,gBAAgB,CAAC;IAC1C,IAAI,CAAC,YAAY;QAAE,OAAO,QAAQ,CAAC;IACnC,OAAO,OAAO,YAAY,KAAK,UAAU;QACvC,CAAC,CAAC,MAAM,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC;QACnC,CAAC,CAAC,YAAY,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,CAAY,EAAE,CAAmB;IACxD,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,OAAO,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CAAC,MAAe;IACjC,MAAM,SAAS,GAAG,MAML,CAAC;IACd,MAAM,IAAI,GAAG,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,EAAE,IAAI,IAAI,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC;IAC9E,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACxD,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAY;IACrC,MAAM,KAAK,GAAG,GAAsD,CAAC;IACrE,MAAM,OAAO,GAAG,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC;IACrC,IACE,KAAK,EAAE,IAAI,KAAK,OAAO;QACvB,CAAC,gCAAgC,CAAC,IAAI,CAAC,OAAO,CAAC,EAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,gCAAgC,CACvC,aAAkB,EAClB,kBAA+B;IAE/B,MAAM,SAAS,GAA4B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACpD,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,GAAkC,EAClC,UAAkB;IAElB,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAS,CAAC;IAC9B,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE7C,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,KAAK,GACT,kBAAkB,CAAC,IAAI,KAAK,CAAC;gBAC3B,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE;gBACb,CAAC,CAAC,EAAE,CAAC,MAAM,CACP,gCAAgC,CAC9B,GAAG,CAAC,aAAa,EACjB,kBAAkB,CACnB,CACF,CAAC;YACR,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,KAAK;iBAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC;iBACvB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;YAC/C,OAAO,QAAQ,IAAI,IAAI,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,IAAI,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,MAAM,GAAG,CAAC;YAC3D,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CACV,aAAa,GAAG,CAAC,IAAI,yCAAyC,OAAO,EAAE,CACxE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,CAAC,IAAI,IAAI,UAAU,qCAAqC,CAC9E,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,YAAoB,EACpB,UAAkB,EAClB,MAAM,GAAkB,aAAa,EAAE;IAEvC,MAAM,GAAG,GAAG,wBAAwB,CAAC,YAAY,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,8BAA8B,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IACjC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAE/D,IACE,mBAAmB;QACnB,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,mBAAmB;QACpE,uBAAuB,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,EAC3C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QAClE,uEAAuE;QACvE,wEAAwE;QACxE,iCAAiC;QACjC,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,EAAE,IAAI,EAAE,eAAe,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC/D,CAAC;IACD,2EAA2E;IAC3E,0EAA0E;IAC1E,mCAAmC;IACnC,IAAI,QAAQ,CAAC,UAAU,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACvE,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACpE,IAAI,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACpC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,GAAkC,EAClC,UAAkB,EAClB,GAAkB,EAClB,QAAa;IAEb,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IACjC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAC/D,IAAI,CAAC,mBAAmB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,CAAC,gCAAgC,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAS,CAAC;IAE9B,MAAM,gBAAgB,GAA6B,EAAE,CAAC;IACtD,IAAI,mBAAmB,EAAE,CAAC;QACxB,gBAAgB,CAAC,IAAI,CACnB,GAAG,CACD,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,aAAa,EAAE,MAAM,CAAC,EACzC,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,mBAAmB,CAAC,CACrE,CACF,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,gBAAgB,CAAC,IAAI,CACnB,GAAG,CACD,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,aAAa,EAAE,KAAK,CAAC,EACxC,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,CACvC,CACF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,EAAE;SAClB,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;SACtC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;SACrB,KAAK,CACJ,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CACzE;SACA,KAAK,CAAC,EAAE,CAAC,CAAC;IAEb,IAAI,IAAI,GAAqB,IAAI,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,IAAkC,EAAE,CAAC;QACnD,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,YAAoB,EACpB,UAAkB,EAClB,OAAO,GAAwB,QAAQ,EACvC,GAAG,GAAkB,aAAa,EAAE;IAEpC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,cAAc,CAAC,gBAAgB,YAAY,IAAI,UAAU,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,cAAc,CACtB,YAAY,OAAO,YAAY,YAAY,IAAI,UAAU,UAAU,MAAM,CAAC,IAAI,GAAG,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\n\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\nexport function resolveRegisteredAccessContext(\n reg: ShareableResourceRegistration | undefined,\n ctx: AccessContext,\n): AccessContext {\n return reg?.resolveAccessContext ? reg.resolveAccessContext(ctx) : ctx;\n}\n\nfunction normalizeEmailForAccess(email: string | undefined): string | null {\n const normalized = email?.trim().toLowerCase();\n return normalized || null;\n}\n\nfunction emailColumnMatches(column: any, email: string): SQL {\n return sql`lower(${column}) = ${email}`;\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n rawCtx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (normalizedUserEmail) {\n clauses.push(\n and(\n emailColumnMatches(resourceTable.ownerEmail, normalizedUserEmail),\n ownerScopeFilter(reg, resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (normalizedUserEmail) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and lower(${sharesTable.principalId}) = ${normalizedUserEmail}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n if (reg?.ownerAccessIgnoresOrg === true) return sql`1=1`;\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(\n reg: ShareableResourceRegistration | undefined,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg?.ownerAccessIgnoresOrg === true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nfunction restrictedShareScopeSql(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n // Restricted resources (extensions) must stay inside their resource org even\n // if stale cross-org share rows already exist from older code or bad data.\n if (reg?.requireOrgMemberForUserShares !== true) return sql`1=1`;\n if (!ctx.orgId) return sql`1=0`;\n return eq(resourceTable.orgId, ctx.orgId);\n}\n\nfunction explicitSharesAllowedForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg.requireOrgMemberForUserShares !== true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\nasync function publicAccessRoleForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): Promise<ShareRole> {\n const roleResolver = reg.publicAccessRole;\n if (!roleResolver) return \"viewer\";\n return typeof roleResolver === \"function\"\n ? await roleResolver(resource, ctx)\n : roleResolver;\n}\n\nfunction higherShareRole(a: ShareRole, b: ShareRole | null): ShareRole {\n if (!b) return a;\n return ROLE_RANK[b] > ROLE_RANK[a] ? b : a;\n}\n\nfunction columnName(column: unknown): string | null {\n const candidate = column as\n | {\n name?: unknown;\n config?: { name?: unknown };\n _: { name?: unknown };\n }\n | undefined;\n const name = candidate?.name ?? candidate?.config?.name ?? candidate?._?.name;\n return typeof name === \"string\" && name ? name : null;\n}\n\nfunction missingColumnName(err: unknown): string | null {\n const error = err as { code?: string; message?: string } | undefined;\n const message = error?.message ?? \"\";\n if (\n error?.code !== \"42703\" &&\n !/no such column|does not exist/i.test(message)\n ) {\n return null;\n }\n const quoted = message.match(/column\\s+\"([^\"]+)\"\\s+does not exist/i)?.[1];\n if (quoted) return quoted;\n const sqlite = message.match(/no such column:\\s+[\"`]?([\\w.]+)[\"`]?/i)?.[1];\n if (sqlite) return sqlite.split(\".\").pop() ?? sqlite;\n return null;\n}\n\nfunction selectAllExistingResourceColumns(\n resourceTable: any,\n omittedColumnNames: Set<string>,\n): Record<string, unknown> {\n const selection: Record<string, unknown> = {};\n for (const [key, column] of Object.entries(resourceTable)) {\n const name = columnName(column);\n if (!name || omittedColumnNames.has(name)) continue;\n selection[key] = column;\n }\n return selection;\n}\n\nasync function loadResourceForAccess(\n reg: ShareableResourceRegistration,\n resourceId: string,\n): Promise<any | null> {\n const db = reg.getDb() as any;\n const omittedColumnNames = new Set<string>();\n\n for (let attempt = 0; attempt < 12; attempt++) {\n try {\n const query =\n omittedColumnNames.size === 0\n ? db.select()\n : db.select(\n selectAllExistingResourceColumns(\n reg.resourceTable,\n omittedColumnNames,\n ),\n );\n const [resource] = await query\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n return resource ?? null;\n } catch (err) {\n const missing = missingColumnName(err);\n if (!missing || omittedColumnNames.has(missing)) throw err;\n omittedColumnNames.add(missing);\n console.warn(\n `[sharing] ${reg.type} access lookup omitted missing column ${missing}`,\n );\n }\n }\n\n throw new Error(\n `Could not load ${reg.type} ${resourceId}: too many missing resource columns`,\n );\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n\n const resource = await loadResourceForAccess(reg, resourceId);\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n\n if (\n normalizedUserEmail &&\n normalizeEmailForAccess(resource.ownerEmail) === normalizedUserEmail &&\n ownerMatchesActiveScope(reg, resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless the resource registration\n // deliberately grants a stronger public-by-link role or explicit shares\n // upgrade the current principal.\n const publicRole = await publicAccessRoleForResource(reg, resource, ctx);\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: higherShareRole(publicRole, role), resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n resource: any,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n if (!normalizedUserEmail && !orgId) return null;\n if (!explicitSharesAllowedForResource(reg, resource, ctx)) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (normalizedUserEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n emailColumnMatches(reg.sharesTable.principalId, normalizedUserEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.61",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|