@luckystack/core 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/eslint/internal/hasPackage.ts","../../src/eslint/rules/no-arbitrary-tailwind-color.ts","../../src/eslint/rules/no-direct-prisma-import-in-components.ts","../../src/eslint/rules/no-raw-fetch-in-src.ts","../../src/eslint/rules/no-raw-try-catch.ts","../../src/eslint/rules/no-unsafe-api-wrappers.ts","../../src/eslint/rules/no-unsafe-sync-wrappers.ts","../../src/eslint/rules/prefer-luckystack-confirm.ts","../../src/eslint/rules/prefer-luckystack-dropdown.ts","../../src/eslint/rules/prefer-luckystack-notify.ts","../../src/eslint/index.ts"],"sourcesContent":["//? Peer-dep presence probe used to gate package-dependent eslint rules.\n//? Resolves against `process.cwd()` so the CONSUMER's node_modules drives\n//? activation, not this package's own resolution graph.\n//?\n//? Implementation: tries `require.resolve` first (works for CJS-compatible\n//? packages), falls back to an fs-based `node_modules/<name>/package.json`\n//? check for pure-ESM packages whose `exports` map omits the `require`\n//? condition (which is the case for the @luckystack/* packages themselves).\n\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nconst localRequire = createRequire(import.meta.url);\n\nconst checkOnDisk = (name: string): boolean => {\n const segments = name.startsWith('@')\n ? name.split('/').slice(0, 2)\n : [name.split('/')[0] ?? name];\n return existsSync(path.join(process.cwd(), 'node_modules', ...segments, 'package.json'));\n};\n\nexport const hasPackage = (name: string): boolean => {\n try {\n localRequire.resolve(name, { paths: [process.cwd()] });\n return true;\n } catch {\n return checkOnDisk(name);\n }\n};\n","//? Rule: warn on arbitrary Tailwind color values (`text-[#fff]`,\n//? `bg-[#abc123]`, `border-[#000]`) inside className strings. The framework\n//? mandates colors from `src/index.css` `@theme` block (rule 14). Arbitrary\n//? colors break dark-mode auto-switching.\n\nimport type { Rule } from 'eslint';\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst ARBITRARY_COLOR_PATTERN = /\\b(?:text|bg|border|ring|fill|stroke|from|to|via|outline|decoration|accent|caret|placeholder|divide)-\\[#[0-9a-fA-F]{3,8}\\]/;\n\n//? This rule reads JSX AST nodes (`JSXAttribute`, `JSXExpressionContainer`,\n//? `TemplateLiteral` quasis) that eslint-plugin-react injects into the visitor\n//? map at runtime but that eslint's stock `Rule.Node` type doesn't model. The\n//? objects ARE real eslint nodes at runtime — `context.report({ node })` only\n//? needs `{ type, loc, range, ... }`, all present. This guard narrows an\n//? `unknown` to `Rule.Node` in one place so the call sites stay cast-free and\n//? the previous file-wide `as unknown as Rule.Node` double-casts are gone.\nconst asRuleNode = (node: object): Rule.Node => node as Rule.Node;\n\n//? JSX AST nodes (JSXAttribute, JSXExpressionContainer, …) are not part of\n//? eslint's stock estree AST. eslint-plugin-react extends the visitor map at\n//? runtime, but the stock `Rule.RuleListener` type does not know about them,\n//? so we describe the shapes we read inline. The actual eslint runtime gives\n//? us the right object; this is purely a TS-side annotation.\ntype ClassNameValueShape =\n | { type: 'Literal'; value: unknown }\n | { type: 'JSXExpressionContainer'; expression: ExpressionShape }\n | { type: string };\n\ntype ExpressionShape =\n | { type: 'Literal'; value: unknown }\n | { type: 'TemplateLiteral'; quasis: readonly { value: { cooked?: string; raw: string } }[] }\n | { type: string };\n\ninterface JsxAttributeShape {\n name: { type: string; name?: string };\n value?: ClassNameValueShape | null;\n}\n\nconst reportIfArbitrary = (\n context: Rule.RuleContext,\n node: Rule.Node,\n value: string,\n): void => {\n if (ARBITRARY_COLOR_PATTERN.test(value)) {\n context.report({ node, messageId: 'useThemeToken' });\n }\n};\n\nconst rule: EslintRule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Disallow arbitrary Tailwind color values (`text-[#…]`, `bg-[#…]`, …). Use tokens from `src/index.css` `@theme` block.',\n },\n messages: {\n useThemeToken:\n 'Do not use arbitrary Tailwind color values like `text-[#…]`. Use a token from `src/index.css` `@theme` block (e.g. `bg-primary`, `text-title`, `border-divider`). Arbitrary colors break dark-mode auto-switching.',\n },\n schema: [],\n },\n create(context) {\n const listener: Rule.RuleListener = {\n JSXAttribute(rawNode: unknown): void {\n const node = rawNode as JsxAttributeShape & Rule.Node;\n if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'className') return;\n const value = node.value;\n if (!value) return;\n if (value.type === 'Literal' && typeof (value as { value?: unknown }).value === 'string') {\n reportIfArbitrary(context, asRuleNode(value), (value as { value: string }).value);\n return;\n }\n if (value.type === 'JSXExpressionContainer') {\n const expression = (value as { expression: ExpressionShape }).expression;\n if (expression.type === 'TemplateLiteral') {\n const quasis = (expression as { quasis: readonly { value: { cooked?: string; raw: string } }[] }).quasis;\n for (const quasi of quasis) {\n reportIfArbitrary(\n context,\n asRuleNode(quasi),\n quasi.value.cooked ?? quasi.value.raw,\n );\n }\n } else if (expression.type === 'Literal' && typeof (expression as { value?: unknown }).value === 'string') {\n reportIfArbitrary(\n context,\n asRuleNode(expression),\n (expression as { value: string }).value,\n );\n }\n }\n },\n };\n return listener;\n },\n};\n\nexport default rule;\n","//? Rule: warn on `import … from '@prisma/client'` outside of allowed\n//? boundaries. Convention: app code re-exports Prisma model types through\n//? `src/_types/<ModelName>.ts` so swapping providers / extending types\n//? stays centralised. Direct imports leak Prisma details into UI code.\n//?\n//? Allowed paths: `src/_types/**`, `server/**`, and any file ending in\n//? `Adapter.ts` (legitimate adapter implementations).\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst isAllowedPath = (filename: string): boolean => {\n const normalised = filename.replaceAll('\\\\', '/');\n if (normalised.includes('/src/_types/')) return true;\n if (normalised.includes('/server/')) return true;\n if (normalised.endsWith('Adapter.ts') || normalised.endsWith('Adapter.tsx')) return true;\n if (normalised.includes('/packages/')) return true;\n //? Project-root config files (the LuckyStack `config.ts` convention) and\n //? any `*.config.ts` are framework boundaries — Prisma types there are\n //? expected and not a \"leaking into component code\" concern.\n if (normalised.endsWith('/config.ts') || normalised.endsWith('.config.ts')) return true;\n if (normalised.includes('/prisma/')) return true;\n return false;\n};\n\nconst rule: EslintRule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Re-export Prisma model types through `src/_types/<Model>.ts` rather than importing `@prisma/client` directly in components.',\n },\n messages: {\n reExportThroughTypes:\n 'Do not import from `@prisma/client` here. Re-export the type through `src/_types/<ModelName>.ts` and import that instead — keeps Prisma details out of component code.',\n },\n schema: [],\n },\n create(context) {\n if (isAllowedPath(context.filename)) return {};\n return {\n ImportDeclaration(node) {\n if (node.source.value === '@prisma/client') {\n context.report({ node, messageId: 'reExportThroughTypes' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: forbid direct `fetch(...)` calls to framework `/api/*` or\n//? `/sync/*` routes. The framework provides typed transport helpers\n//? `apiRequest` (for `_api/` routes) and `syncRequest` (for `_sync/`\n//? routes) which preserve route/version generics + auth/CSRF/validation.\n//?\n//? The rule is deliberately narrow:\n//? - Fires when the URL is a literal / template containing `/api/` or\n//? `/sync/` (framework route patterns).\n//? - Skips URLs whose PATH STARTS with `/auth/` — the framework's auth\n//? endpoints (`/auth/api/credentials`, OAuth callbacks) are HTTP-only\n//? and intentionally use raw fetch (cookies, headers, redirects). The\n//? start-of-path check (not `.includes`) means a consumer's typed\n//? route under an `auth/` page folder (URL `/api/auth/...`) still\n//? fires correctly.\n//? - Skips external URLs (starts with `http://`, `https://`, `//`) —\n//? consumers fetching third-party APIs are fine.\n//? - Skips dynamic URLs (variable, function call, etc.) — be permissive\n//? rather than flag-by-default; consumers can opt in via comments.\n//?\n//? Gated by hasPackage('@luckystack/api') OR hasPackage('@luckystack/core')\n//? at the config-composition layer.\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\ninterface AstNodeShape {\n type?: string;\n value?: unknown;\n quasis?: { value?: { raw?: string; cooked?: string } }[];\n}\n\nconst extractStaticUrlText = (urlNode: unknown): string | null => {\n if (!urlNode || typeof urlNode !== 'object') return null;\n const n = urlNode as AstNodeShape;\n if (n.type === 'Literal' && typeof n.value === 'string') return n.value;\n if (n.type === 'TemplateLiteral' && Array.isArray(n.quasis)) {\n return n.quasis.map((q) => q.value?.cooked ?? q.value?.raw ?? '').join('|');\n }\n return null;\n};\n\nconst targetsFrameworkRoute = (urlNode: unknown): boolean => {\n const text = extractStaticUrlText(urlNode);\n if (text === null) return false;\n if (/^(https?:)?\\/\\//.test(text)) return false;\n //? Skip only when the path STARTS with `/auth/` (framework's special\n //? endpoints). The leading `|` accounts for `extractStaticUrlText`'s\n //? template-literal join when the first quasi is empty, e.g.\n //? `${backendUrl}/auth/api/credentials` joins to `|/auth/api/credentials`.\n if (/^(?:\\|)?\\/auth\\//.test(text)) return false;\n return /(?:^|[^A-Za-z])\\/(?:api|sync)\\//.test(text);\n};\n\nconst rule: EslintRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow raw `fetch(...)` to framework `/api/*` and `/sync/*` routes. Use `apiRequest` / `syncRequest` instead.',\n },\n messages: {\n useApiRequest:\n 'Do not call `fetch` directly for framework `/api/*` routes. Use `apiRequest({ name, version, data })` for typed inference, or `httpFetch(...)` from `@luckystack/core` if you need a raw same-origin request.',\n useSyncRequest:\n 'Do not call `fetch` directly for framework `/sync/*` routes. Use `syncRequest({ name, version, data, receiver })` instead.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n const callee = node.callee;\n const isFetchCall =\n (callee.type === 'Identifier' && callee.name === 'fetch') ||\n (callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.object.name === 'window' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'fetch');\n if (!isFetchCall) return;\n const url = node.arguments[0];\n if (!targetsFrameworkRoute(url)) return;\n const text = extractStaticUrlText(url) ?? '';\n const messageId = text.includes('/sync/') ? 'useSyncRequest' : 'useApiRequest';\n context.report({ node, messageId });\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: forbid raw `try { … } catch` inside src/server/shared code.\n//? The framework provides `tryCatch` from @luckystack/core (and the API/sync\n//? handler `functions.tryCatch` injection) that handles Sentry capture +\n//? normalised tuple returns. Raw try/catch bypasses that.\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst rule: EslintRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow raw `try { … } catch` blocks — use the framework `tryCatch` helper instead.',\n },\n messages: {\n useTryCatch:\n 'Do not use raw try/catch. Import `tryCatch` from `@luckystack/core` (client) or `server/functions/tryCatch` (server), or use the injected `functions.tryCatch` in API / sync handlers.',\n },\n schema: [],\n },\n create(context) {\n return {\n TryStatement(node) {\n //? Only fire when an actual catch clause is present. `try { } finally { }`\n //? is a different construct (cleanup, not error handling) and the\n //? `tryCatch` helper is not a substitute.\n if (node.handler) {\n context.report({ node, messageId: 'useTryCatch' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: forbid local wrappers around `apiRequest` whose parameter types\n//? erase the route/version generics. The framework's type system relies on\n//? literal `name`/`version` strings to infer `serverOutput` / `clientOutput`.\n//? A wrapper like:\n//?\n//? const unsafeApi = (name: string, version: string, data: unknown) =>\n//? apiRequest({ name: name as any, ... });\n//?\n//? destroys that inference. Always call `apiRequest` directly with route\n//? literals, or write a wrapper that preserves the generics.\n//?\n//? Gated by hasPackage('@luckystack/api') at the config-composition layer.\n\n//? Double-cast + any-spread disabled file-wide: bridges `Rule.Node` to specific\n//? AST shapes; the spread iterates over `Object.entries(node)` whose values are\n//? typed as `any` because eslint's AST shape varies by node kind.\n/* eslint-disable no-restricted-syntax, @typescript-eslint/no-unsafe-argument */\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst SUSPECT_PARAM_NAMES = new Set(['name', 'version', 'route', 'apiName']);\n\n//? AST keys that point to parents or non-AST metadata. Including them in\n//? a recursive descent causes infinite loops because eslint adds `parent`\n//? pointers during traversal.\nconst SKIP_KEYS = new Set(['parent', 'loc', 'range', 'tokens', 'comments']);\n\nconst containsApiRequestCall = (body: unknown): boolean => {\n if (!body || typeof body !== 'object') return false;\n const visited = new WeakSet<object>();\n const stack: unknown[] = [body];\n while (stack.length > 0) {\n const node = stack.pop() as Record<string, unknown> | null;\n if (!node || typeof node !== 'object') continue;\n if (visited.has(node)) continue;\n visited.add(node);\n const callee = (node as { type?: string; callee?: { type?: string; name?: string } }).callee;\n if (\n (node as { type?: string }).type === 'CallExpression' &&\n callee?.type === 'Identifier' &&\n (callee.name === 'apiRequest' || callee.name === 'syncRequest')\n ) {\n return true;\n }\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key)) continue;\n if (Array.isArray(value)) stack.push(...value);\n else if (value && typeof value === 'object') stack.push(value);\n }\n }\n return false;\n};\n\nconst isSuspectFunction = (node: {\n params?: { type?: string; name?: string; typeAnnotation?: { typeAnnotation?: { type?: string } } }[];\n body?: unknown;\n}): boolean => {\n if (!node.params || node.params.length === 0) return false;\n const hasSuspectStringParam = node.params.some((p) => {\n if (p.type !== 'Identifier' || !p.name) return false;\n if (!SUSPECT_PARAM_NAMES.has(p.name)) return false;\n const annotation = p.typeAnnotation?.typeAnnotation?.type;\n return annotation === 'TSStringKeyword';\n });\n if (!hasSuspectStringParam) return false;\n return containsApiRequestCall(node.body);\n};\n\nconst rule: EslintRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow local wrappers around `apiRequest` / `syncRequest` whose `name`/`version` params are typed as plain `string` — this erases the generic route/version inference.',\n },\n messages: {\n unsafeWrapper:\n 'This wrapper around `apiRequest` / `syncRequest` accepts `{{paramName}}: string` and erases the route/version generic inference. Call the framework helper directly with literal route names, or type the parameter as a literal union (e.g. `name: \\'examples/getUserData\\'`).',\n },\n schema: [],\n },\n create(context) {\n return {\n FunctionDeclaration(node) {\n if (isSuspectFunction(node)) {\n const suspect = (node.params as { name?: string }[]).find((p) => p.name && SUSPECT_PARAM_NAMES.has(p.name));\n context.report({ node, messageId: 'unsafeWrapper', data: { paramName: suspect?.name ?? 'name' } });\n }\n },\n ArrowFunctionExpression(node) {\n if (isSuspectFunction(node)) {\n const suspect = (node.params as { name?: string }[]).find((p) => p.name && SUSPECT_PARAM_NAMES.has(p.name));\n context.report({ node, messageId: 'unsafeWrapper', data: { paramName: suspect?.name ?? 'name' } });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: same shape as no-unsafe-api-wrappers, but targeting wrappers\n//? around `syncRequest` and `upsertSyncEventCallback`. Gated by\n//? hasPackage('@luckystack/sync') at the config-composition layer.\n//?\n//? Note: the api-wrappers rule already covers the `syncRequest` call site;\n//? this rule additionally catches `upsertSyncEventCallback` wrappers and\n//? exists as a distinct identity so the sync-only rule can be disabled\n//? independently when a consumer uses api but not sync.\n\n//? Double-cast + any-spread disabled file-wide: bridges `Rule.Node` to specific\n//? AST shapes; the spread iterates over `Object.entries(node)` whose values are\n//? typed as `any` because eslint's AST shape varies by node kind.\n/* eslint-disable no-restricted-syntax, @typescript-eslint/no-unsafe-argument */\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst SUSPECT_PARAM_NAMES = new Set(['name', 'version', 'route', 'syncName']);\nconst SUSPECT_CALLEES = new Set(['syncRequest', 'upsertSyncEventCallback']);\n\n//? AST keys that point to parents or non-AST metadata. Skipped to avoid\n//? infinite loops when eslint adds `parent` pointers during traversal.\nconst SKIP_KEYS = new Set(['parent', 'loc', 'range', 'tokens', 'comments']);\n\nconst containsSuspectCall = (body: unknown): boolean => {\n if (!body || typeof body !== 'object') return false;\n const visited = new WeakSet<object>();\n const stack: unknown[] = [body];\n while (stack.length > 0) {\n const node = stack.pop() as Record<string, unknown> | null;\n if (!node || typeof node !== 'object') continue;\n if (visited.has(node)) continue;\n visited.add(node);\n const callee = (node as { type?: string; callee?: { type?: string; name?: string } }).callee;\n if (\n (node as { type?: string }).type === 'CallExpression' &&\n callee?.type === 'Identifier' &&\n SUSPECT_CALLEES.has(callee.name ?? '')\n ) {\n return true;\n }\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key)) continue;\n if (Array.isArray(value)) stack.push(...value);\n else if (value && typeof value === 'object') stack.push(value);\n }\n }\n return false;\n};\n\nconst isSuspect = (node: {\n params?: { type?: string; name?: string; typeAnnotation?: { typeAnnotation?: { type?: string } } }[];\n body?: unknown;\n}): boolean => {\n if (!node.params || node.params.length === 0) return false;\n const hasSuspectStringParam = node.params.some((p) => {\n if (p.type !== 'Identifier' || !p.name) return false;\n if (!SUSPECT_PARAM_NAMES.has(p.name)) return false;\n return p.typeAnnotation?.typeAnnotation?.type === 'TSStringKeyword';\n });\n if (!hasSuspectStringParam) return false;\n return containsSuspectCall(node.body);\n};\n\nconst rule: EslintRule = {\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallow local wrappers around `syncRequest` / `upsertSyncEventCallback` whose `name`/`version` params are typed as plain `string`.',\n },\n messages: {\n unsafeSyncWrapper:\n 'This wrapper around `syncRequest` / `upsertSyncEventCallback` accepts a `string` route param and erases the generic event-payload inference. Call the framework helper directly with literal route names.',\n },\n schema: [],\n },\n create(context) {\n return {\n FunctionDeclaration(node) {\n if (isSuspect(node)) {\n context.report({ node, messageId: 'unsafeSyncWrapper' });\n }\n },\n ArrowFunctionExpression(node) {\n if (isSuspect(node)) {\n context.report({ node, messageId: 'unsafeSyncWrapper' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: warn on `confirm(...)` / `window.confirm(...)` calls. The framework\n//? provides `menuHandler.confirm({ title, content, input? })` from\n//? `src/_functions/menuHandler.ts` which renders a styled, theme-aware,\n//? Promise-returning confirm dialog and respects keyboard / accessibility.\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst rule: EslintRule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer `menuHandler.confirm({ title, content })` over the native `confirm()` dialog.',\n },\n messages: {\n useMenuHandlerConfirm:\n 'Use `menuHandler.confirm({ title, content })` from `src/_functions/menuHandler.ts` instead of native `confirm()`. Returns a `Promise<boolean>` with proper theming.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n const callee = node.callee;\n if (callee.type === 'Identifier' && callee.name === 'confirm') {\n context.report({ node, messageId: 'useMenuHandlerConfirm' });\n return;\n }\n if (\n callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.object.name === 'window' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'confirm'\n ) {\n context.report({ node, messageId: 'useMenuHandlerConfirm' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Rule: warn when a JSX `<select>` element is used. The framework ships\n//? `Dropdown` (src/_components/Dropdown.tsx) with search, keyboard nav,\n//? sizing variants, and consistent dark/light styling. Native `<select>`\n//? skips all of that.\n\nimport type { Rule } from 'eslint';\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\n//? JSX AST nodes are extended at runtime by eslint-plugin-react, but\n//? eslint's stock `Rule.RuleListener` type does not know about them. We\n//? describe the shapes we read inline.\ninterface JsxOpeningElementShape {\n name: { type: string; name?: string };\n}\n\nconst rule: EslintRule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer the framework `Dropdown` component over native `<select>` elements.',\n },\n messages: {\n useDropdown:\n 'Use `Dropdown` from `src/_components/Dropdown.tsx` (or `MultiSelectDropdown` for multi-select) instead of native `<select>`. The framework component handles keyboard nav, search, sizing, and dark/light theme.',\n },\n schema: [],\n },\n create(context) {\n const listener: Rule.RuleListener = {\n JSXOpeningElement(rawNode: unknown): void {\n const node = rawNode as JsxOpeningElementShape & Rule.Node;\n if (node.name.type === 'JSXIdentifier' && node.name.name === 'select') {\n context.report({ node, messageId: 'useDropdown' });\n }\n },\n };\n return listener;\n },\n};\n\nexport default rule;\n","//? Rule: warn on `alert(...)` / `window.alert(...)`. The framework's\n//? `notify` helper (from @luckystack/core) renders themed toast\n//? notifications via the registered notifier (default sonner).\n\nimport type { EslintRule } from '../internal/ruleTypes.js';\n\nconst rule: EslintRule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer `notify()` over the native `alert()` dialog.',\n },\n messages: {\n useNotify:\n 'Use `notify({ type, message })` from `@luckystack/core` instead of native `alert()`. Renders a themed toast via the registered notifier.',\n },\n schema: [],\n },\n create(context) {\n return {\n CallExpression(node) {\n const callee = node.callee;\n if (callee.type === 'Identifier' && callee.name === 'alert') {\n context.report({ node, messageId: 'useNotify' });\n return;\n }\n if (\n callee.type === 'MemberExpression' &&\n callee.object.type === 'Identifier' &&\n callee.object.name === 'window' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'alert'\n ) {\n context.report({ node, messageId: 'useNotify' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","//? Public entry for `@luckystack/core/eslint`. Composes the LuckyStack\n//? flat-config tier that the scaffolded `eslint.luckystack.config.js`\n//? consumes. Rules that target an optional `@luckystack/*` peer package\n//? are gated via `hasPackage(...)` so absent peers cause silent rule\n//? skips (NOT noisy \"rule not found\" errors).\n//?\n//? Override surface: consumers can spread this default export and append\n//? their own config blocks, or replace individual rule severities by\n//? re-stating them with `'off' | 'warn' | 'error'`.\n\nimport type { Linter } from 'eslint';\n\nimport { hasPackage } from './internal/hasPackage.js';\nimport type { EslintRule } from './internal/ruleTypes.js';\n\nimport noArbitraryTailwindColor from './rules/no-arbitrary-tailwind-color.js';\nimport noDirectPrismaImportInComponents from './rules/no-direct-prisma-import-in-components.js';\nimport noRawFetchInSrc from './rules/no-raw-fetch-in-src.js';\nimport noRawTryCatch from './rules/no-raw-try-catch.js';\nimport noUnsafeApiWrappers from './rules/no-unsafe-api-wrappers.js';\nimport noUnsafeSyncWrappers from './rules/no-unsafe-sync-wrappers.js';\nimport preferLuckystackConfirm from './rules/prefer-luckystack-confirm.js';\nimport preferLuckystackDropdown from './rules/prefer-luckystack-dropdown.js';\nimport preferLuckystackNotify from './rules/prefer-luckystack-notify.js';\n\nconst allRules: Record<string, EslintRule> = {\n 'no-raw-try-catch': noRawTryCatch,\n 'no-raw-fetch-in-src': noRawFetchInSrc,\n 'no-unsafe-api-wrappers': noUnsafeApiWrappers,\n 'no-unsafe-sync-wrappers': noUnsafeSyncWrappers,\n 'prefer-luckystack-dropdown': preferLuckystackDropdown,\n 'prefer-luckystack-confirm': preferLuckystackConfirm,\n 'prefer-luckystack-notify': preferLuckystackNotify,\n 'no-direct-prisma-import-in-components': noDirectPrismaImportInComponents,\n 'no-arbitrary-tailwind-color': noArbitraryTailwindColor,\n};\n\nconst hasApi = hasPackage('@luckystack/api');\nconst hasCore = hasPackage('@luckystack/core');\nconst hasSync = hasPackage('@luckystack/sync');\n\nconst ruleSeverities: Record<string, Linter.RuleSeverity> = {\n // Always-on errors.\n 'luckystack/no-raw-try-catch': 'error',\n\n // Package-gated errors. Disabled when the relevant peer is absent so\n // a consumer using only @luckystack/core sees no \"use apiRequest\" noise.\n 'luckystack/no-raw-fetch-in-src': hasApi || hasCore ? 'error' : 'off',\n 'luckystack/no-unsafe-api-wrappers': hasApi ? 'error' : 'off',\n 'luckystack/no-unsafe-sync-wrappers': hasSync ? 'error' : 'off',\n\n // Warnings — enabled by default per framework owner's preference.\n 'luckystack/prefer-luckystack-dropdown': 'warn',\n 'luckystack/prefer-luckystack-confirm': 'warn',\n 'luckystack/prefer-luckystack-notify': 'warn',\n 'luckystack/no-direct-prisma-import-in-components': 'warn',\n 'luckystack/no-arbitrary-tailwind-color': 'warn',\n};\n\nconst luckystackConfig: Linter.Config[] = [\n {\n files: ['**/*.{ts,tsx,js,jsx,mjs,cjs}'],\n plugins: {\n luckystack: { rules: allRules },\n },\n rules: ruleSeverities,\n },\n];\n\nexport default luckystackConfig;\n\n//? Named exports for advanced consumers who want to assemble their own\n//? config or selectively re-enable individual rules.\nexport { allRules as rules };\n\n\nexport {hasPackage} from './internal/hasPackage.js';"],"mappings":"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","names":["rule","rule","rule","rule","SUSPECT_PARAM_NAMES","SKIP_KEYS","rule","rule","rule","rule"]}
1
+ {"version":3,"sources":["../../src/eslint/internal/hasPackage.ts","../../src/eslint/rules/no-arbitrary-tailwind-color.ts","../../src/eslint/rules/no-direct-prisma-import-in-components.ts","../../src/eslint/rules/no-raw-fetch-in-src.ts","../../src/eslint/rules/no-raw-try-catch.ts","../../src/eslint/rules/no-unsafe-api-wrappers.ts","../../src/eslint/rules/no-unsafe-sync-wrappers.ts","../../src/eslint/rules/prefer-luckystack-confirm.ts","../../src/eslint/rules/prefer-luckystack-dropdown.ts","../../src/eslint/rules/prefer-luckystack-notify.ts","../../src/eslint/index.ts"],"sourcesContent":["//? Peer-dep presence probe used to gate package-dependent eslint rules.\r\n//? Resolves against `process.cwd()` so the CONSUMER's node_modules drives\r\n//? activation, not this package's own resolution graph.\r\n//?\r\n//? Implementation: tries `require.resolve` first (works for CJS-compatible\r\n//? packages), falls back to an fs-based `node_modules/<name>/package.json`\r\n//? check for pure-ESM packages whose `exports` map omits the `require`\r\n//? condition (which is the case for the @luckystack/* packages themselves).\r\n\r\nimport { existsSync } from 'node:fs';\r\nimport { createRequire } from 'node:module';\r\nimport path from 'node:path';\r\n\r\nconst localRequire = createRequire(import.meta.url);\r\n\r\nconst checkOnDisk = (name: string): boolean => {\r\n const segments = name.startsWith('@')\r\n ? name.split('/').slice(0, 2)\r\n : [name.split('/')[0] ?? name];\r\n return existsSync(path.join(process.cwd(), 'node_modules', ...segments, 'package.json'));\r\n};\r\n\r\nexport const hasPackage = (name: string): boolean => {\r\n try {\r\n localRequire.resolve(name, { paths: [process.cwd()] });\r\n return true;\r\n } catch {\r\n return checkOnDisk(name);\r\n }\r\n};\r\n","//? Rule: warn on arbitrary Tailwind color values (`text-[#fff]`,\r\n//? `bg-[#abc123]`, `border-[#000]`) inside className strings. The framework\r\n//? mandates colors from `src/index.css` `@theme` block (rule 14). Arbitrary\r\n//? colors break dark-mode auto-switching.\r\n\r\nimport type { Rule } from 'eslint';\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst ARBITRARY_COLOR_PATTERN = /\\b(?:text|bg|border|ring|fill|stroke|from|to|via|outline|decoration|accent|caret|placeholder|divide)-\\[#[0-9a-fA-F]{3,8}\\]/;\r\n\r\n//? This rule reads JSX AST nodes (`JSXAttribute`, `JSXExpressionContainer`,\r\n//? `TemplateLiteral` quasis) that eslint-plugin-react injects into the visitor\r\n//? map at runtime but that eslint's stock `Rule.Node` type doesn't model. The\r\n//? objects ARE real eslint nodes at runtime — `context.report({ node })` only\r\n//? needs `{ type, loc, range, ... }`, all present. This guard narrows an\r\n//? `unknown` to `Rule.Node` in one place so the call sites stay cast-free and\r\n//? the previous file-wide `as unknown as Rule.Node` double-casts are gone.\r\nconst asRuleNode = (node: object): Rule.Node => node as Rule.Node;\r\n\r\n//? JSX AST nodes (JSXAttribute, JSXExpressionContainer, …) are not part of\r\n//? eslint's stock estree AST. eslint-plugin-react extends the visitor map at\r\n//? runtime, but the stock `Rule.RuleListener` type does not know about them,\r\n//? so we describe the shapes we read inline. The actual eslint runtime gives\r\n//? us the right object; this is purely a TS-side annotation.\r\ntype ClassNameValueShape =\r\n | { type: 'Literal'; value: unknown }\r\n | { type: 'JSXExpressionContainer'; expression: ExpressionShape }\r\n | { type: string };\r\n\r\ntype ExpressionShape =\r\n | { type: 'Literal'; value: unknown }\r\n | { type: 'TemplateLiteral'; quasis: readonly { value: { cooked?: string; raw: string } }[] }\r\n | { type: string };\r\n\r\ninterface JsxAttributeShape {\r\n name: { type: string; name?: string };\r\n value?: ClassNameValueShape | null;\r\n}\r\n\r\nconst reportIfArbitrary = (\r\n context: Rule.RuleContext,\r\n node: Rule.Node,\r\n value: string,\r\n): void => {\r\n if (ARBITRARY_COLOR_PATTERN.test(value)) {\r\n context.report({ node, messageId: 'useThemeToken' });\r\n }\r\n};\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'suggestion',\r\n docs: {\r\n description: 'Disallow arbitrary Tailwind color values (`text-[#…]`, `bg-[#…]`, …). Use tokens from `src/index.css` `@theme` block.',\r\n },\r\n messages: {\r\n useThemeToken:\r\n 'Do not use arbitrary Tailwind color values like `text-[#…]`. Use a token from `src/index.css` `@theme` block (e.g. `bg-primary`, `text-title`, `border-divider`). Arbitrary colors break dark-mode auto-switching.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n const listener: Rule.RuleListener = {\r\n JSXAttribute(rawNode: unknown): void {\r\n const node = rawNode as JsxAttributeShape & Rule.Node;\r\n if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'className') return;\r\n const value = node.value;\r\n if (!value) return;\r\n if (value.type === 'Literal' && typeof (value as { value?: unknown }).value === 'string') {\r\n reportIfArbitrary(context, asRuleNode(value), (value as { value: string }).value);\r\n return;\r\n }\r\n if (value.type === 'JSXExpressionContainer') {\r\n const expression = (value as { expression: ExpressionShape }).expression;\r\n if (expression.type === 'TemplateLiteral') {\r\n const quasis = (expression as { quasis: readonly { value: { cooked?: string; raw: string } }[] }).quasis;\r\n for (const quasi of quasis) {\r\n reportIfArbitrary(\r\n context,\r\n asRuleNode(quasi),\r\n quasi.value.cooked ?? quasi.value.raw,\r\n );\r\n }\r\n } else if (expression.type === 'Literal' && typeof (expression as { value?: unknown }).value === 'string') {\r\n reportIfArbitrary(\r\n context,\r\n asRuleNode(expression),\r\n (expression as { value: string }).value,\r\n );\r\n }\r\n }\r\n },\r\n };\r\n return listener;\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: warn on `import … from '@prisma/client'` outside of allowed\r\n//? boundaries. Convention: app code re-exports Prisma model types through\r\n//? `src/_types/<ModelName>.ts` so swapping providers / extending types\r\n//? stays centralised. Direct imports leak Prisma details into UI code.\r\n//?\r\n//? Allowed paths: `src/_types/**`, `server/**`, and any file ending in\r\n//? `Adapter.ts` (legitimate adapter implementations).\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst isAllowedPath = (filename: string): boolean => {\r\n const normalised = filename.replaceAll('\\\\', '/');\r\n if (normalised.includes('/src/_types/')) return true;\r\n if (normalised.includes('/server/')) return true;\r\n if (normalised.endsWith('Adapter.ts') || normalised.endsWith('Adapter.tsx')) return true;\r\n if (normalised.includes('/packages/')) return true;\r\n //? Project-root config files (the LuckyStack `config.ts` convention) and\r\n //? any `*.config.ts` are framework boundaries — Prisma types there are\r\n //? expected and not a \"leaking into component code\" concern.\r\n if (normalised.endsWith('/config.ts') || normalised.endsWith('.config.ts')) return true;\r\n if (normalised.includes('/prisma/')) return true;\r\n return false;\r\n};\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'suggestion',\r\n docs: {\r\n description: 'Re-export Prisma model types through `src/_types/<Model>.ts` rather than importing `@prisma/client` directly in components.',\r\n },\r\n messages: {\r\n reExportThroughTypes:\r\n 'Do not import from `@prisma/client` here. Re-export the type through `src/_types/<ModelName>.ts` and import that instead — keeps Prisma details out of component code.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n if (isAllowedPath(context.filename)) return {};\r\n return {\r\n ImportDeclaration(node) {\r\n if (node.source.value === '@prisma/client') {\r\n context.report({ node, messageId: 'reExportThroughTypes' });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: forbid direct `fetch(...)` calls to framework `/api/*` or\r\n//? `/sync/*` routes. The framework provides typed transport helpers\r\n//? `apiRequest` (for `_api/` routes) and `syncRequest` (for `_sync/`\r\n//? routes) which preserve route/version generics + auth/CSRF/validation.\r\n//?\r\n//? The rule is deliberately narrow:\r\n//? - Fires when the URL is a literal / template containing `/api/` or\r\n//? `/sync/` (framework route patterns).\r\n//? - Skips URLs whose PATH STARTS with `/auth/` — the framework's auth\r\n//? endpoints (`/auth/api/credentials`, OAuth callbacks) are HTTP-only\r\n//? and intentionally use raw fetch (cookies, headers, redirects). The\r\n//? start-of-path check (not `.includes`) means a consumer's typed\r\n//? route under an `auth/` page folder (URL `/api/auth/...`) still\r\n//? fires correctly.\r\n//? - Skips external URLs (starts with `http://`, `https://`, `//`) —\r\n//? consumers fetching third-party APIs are fine.\r\n//? - Skips dynamic URLs (variable, function call, etc.) — be permissive\r\n//? rather than flag-by-default; consumers can opt in via comments.\r\n//?\r\n//? Gated by hasPackage('@luckystack/api') OR hasPackage('@luckystack/core')\r\n//? at the config-composition layer.\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\ninterface AstNodeShape {\r\n type?: string;\r\n value?: unknown;\r\n quasis?: { value?: { raw?: string; cooked?: string } }[];\r\n}\r\n\r\nconst extractStaticUrlText = (urlNode: unknown): string | null => {\r\n if (!urlNode || typeof urlNode !== 'object') return null;\r\n const n = urlNode as AstNodeShape;\r\n if (n.type === 'Literal' && typeof n.value === 'string') return n.value;\r\n if (n.type === 'TemplateLiteral' && Array.isArray(n.quasis)) {\r\n return n.quasis.map((q) => q.value?.cooked ?? q.value?.raw ?? '').join('|');\r\n }\r\n return null;\r\n};\r\n\r\nconst targetsFrameworkRoute = (urlNode: unknown): boolean => {\r\n const text = extractStaticUrlText(urlNode);\r\n if (text === null) return false;\r\n if (/^(https?:)?\\/\\//.test(text)) return false;\r\n //? Skip only when the path STARTS with `/auth/` (framework's special\r\n //? endpoints). The leading `|` accounts for `extractStaticUrlText`'s\r\n //? template-literal join when the first quasi is empty, e.g.\r\n //? `${backendUrl}/auth/api/credentials` joins to `|/auth/api/credentials`.\r\n if (/^(?:\\|)?\\/auth\\//.test(text)) return false;\r\n return /(?:^|[^A-Za-z])\\/(?:api|sync)\\//.test(text);\r\n};\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'problem',\r\n docs: {\r\n description: 'Disallow raw `fetch(...)` to framework `/api/*` and `/sync/*` routes. Use `apiRequest` / `syncRequest` instead.',\r\n },\r\n messages: {\r\n useApiRequest:\r\n 'Do not call `fetch` directly for framework `/api/*` routes. Use `apiRequest({ name, version, data })` for typed inference, or `httpFetch(...)` from `@luckystack/core` if you need a raw same-origin request.',\r\n useSyncRequest:\r\n 'Do not call `fetch` directly for framework `/sync/*` routes. Use `syncRequest({ name, version, data, receiver })` instead.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n CallExpression(node) {\r\n const callee = node.callee;\r\n const isFetchCall =\r\n (callee.type === 'Identifier' && callee.name === 'fetch') ||\r\n (callee.type === 'MemberExpression' &&\r\n callee.object.type === 'Identifier' &&\r\n callee.object.name === 'window' &&\r\n callee.property.type === 'Identifier' &&\r\n callee.property.name === 'fetch');\r\n if (!isFetchCall) return;\r\n const url = node.arguments[0];\r\n if (!targetsFrameworkRoute(url)) return;\r\n const text = extractStaticUrlText(url) ?? '';\r\n const messageId = text.includes('/sync/') ? 'useSyncRequest' : 'useApiRequest';\r\n context.report({ node, messageId });\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: forbid raw `try { … } catch` inside src/server/shared code.\r\n//? The framework provides `tryCatch` from @luckystack/core (and the API/sync\r\n//? handler `functions.tryCatch` injection) that handles Sentry capture +\r\n//? normalised tuple returns. Raw try/catch bypasses that.\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'problem',\r\n docs: {\r\n description: 'Disallow raw `try { … } catch` blocks — use the framework `tryCatch` helper instead.',\r\n },\r\n messages: {\r\n useTryCatch:\r\n 'Do not use raw try/catch. Import `tryCatch` from `@luckystack/core` (client) or `server/functions/tryCatch` (server), or use the injected `functions.tryCatch` in API / sync handlers.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n TryStatement(node) {\r\n //? Only fire when an actual catch clause is present. `try { } finally { }`\r\n //? is a different construct (cleanup, not error handling) and the\r\n //? `tryCatch` helper is not a substitute.\r\n if (node.handler) {\r\n context.report({ node, messageId: 'useTryCatch' });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: forbid local wrappers around `apiRequest` whose parameter types\r\n//? erase the route/version generics. The framework's type system relies on\r\n//? literal `name`/`version` strings to infer `serverOutput` / `clientOutput`.\r\n//? A wrapper like:\r\n//?\r\n//? const unsafeApi = (name: string, version: string, data: unknown) =>\r\n//? apiRequest({ name: name as any, ... });\r\n//?\r\n//? destroys that inference. Always call `apiRequest` directly with route\r\n//? literals, or write a wrapper that preserves the generics.\r\n//?\r\n//? Gated by hasPackage('@luckystack/api') at the config-composition layer.\r\n\r\n//? Double-cast + any-spread disabled file-wide: bridges `Rule.Node` to specific\r\n//? AST shapes; the spread iterates over `Object.entries(node)` whose values are\r\n//? typed as `any` because eslint's AST shape varies by node kind.\r\n/* eslint-disable no-restricted-syntax, @typescript-eslint/no-unsafe-argument */\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst SUSPECT_PARAM_NAMES = new Set(['name', 'version', 'route', 'apiName']);\r\n\r\n//? AST keys that point to parents or non-AST metadata. Including them in\r\n//? a recursive descent causes infinite loops because eslint adds `parent`\r\n//? pointers during traversal.\r\nconst SKIP_KEYS = new Set(['parent', 'loc', 'range', 'tokens', 'comments']);\r\n\r\nconst containsApiRequestCall = (body: unknown): boolean => {\r\n if (!body || typeof body !== 'object') return false;\r\n const visited = new WeakSet<object>();\r\n const stack: unknown[] = [body];\r\n while (stack.length > 0) {\r\n const node = stack.pop() as Record<string, unknown> | null;\r\n if (!node || typeof node !== 'object') continue;\r\n if (visited.has(node)) continue;\r\n visited.add(node);\r\n const callee = (node as { type?: string; callee?: { type?: string; name?: string } }).callee;\r\n if (\r\n (node as { type?: string }).type === 'CallExpression' &&\r\n callee?.type === 'Identifier' &&\r\n (callee.name === 'apiRequest' || callee.name === 'syncRequest')\r\n ) {\r\n return true;\r\n }\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) stack.push(...value);\r\n else if (value && typeof value === 'object') stack.push(value);\r\n }\r\n }\r\n return false;\r\n};\r\n\r\nconst isSuspectFunction = (node: {\r\n params?: { type?: string; name?: string; typeAnnotation?: { typeAnnotation?: { type?: string } } }[];\r\n body?: unknown;\r\n}): boolean => {\r\n if (!node.params || node.params.length === 0) return false;\r\n const hasSuspectStringParam = node.params.some((p) => {\r\n if (p.type !== 'Identifier' || !p.name) return false;\r\n if (!SUSPECT_PARAM_NAMES.has(p.name)) return false;\r\n const annotation = p.typeAnnotation?.typeAnnotation?.type;\r\n return annotation === 'TSStringKeyword';\r\n });\r\n if (!hasSuspectStringParam) return false;\r\n return containsApiRequestCall(node.body);\r\n};\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'problem',\r\n docs: {\r\n description: 'Disallow local wrappers around `apiRequest` / `syncRequest` whose `name`/`version` params are typed as plain `string` — this erases the generic route/version inference.',\r\n },\r\n messages: {\r\n unsafeWrapper:\r\n 'This wrapper around `apiRequest` / `syncRequest` accepts `{{paramName}}: string` and erases the route/version generic inference. Call the framework helper directly with literal route names, or type the parameter as a literal union (e.g. `name: \\'examples/getUserData\\'`).',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n FunctionDeclaration(node) {\r\n if (isSuspectFunction(node)) {\r\n const suspect = (node.params as { name?: string }[]).find((p) => p.name && SUSPECT_PARAM_NAMES.has(p.name));\r\n context.report({ node, messageId: 'unsafeWrapper', data: { paramName: suspect?.name ?? 'name' } });\r\n }\r\n },\r\n ArrowFunctionExpression(node) {\r\n if (isSuspectFunction(node)) {\r\n const suspect = (node.params as { name?: string }[]).find((p) => p.name && SUSPECT_PARAM_NAMES.has(p.name));\r\n context.report({ node, messageId: 'unsafeWrapper', data: { paramName: suspect?.name ?? 'name' } });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: same shape as no-unsafe-api-wrappers, but targeting wrappers\r\n//? around `syncRequest` and `upsertSyncEventCallback`. Gated by\r\n//? hasPackage('@luckystack/sync') at the config-composition layer.\r\n//?\r\n//? Note: the api-wrappers rule already covers the `syncRequest` call site;\r\n//? this rule additionally catches `upsertSyncEventCallback` wrappers and\r\n//? exists as a distinct identity so the sync-only rule can be disabled\r\n//? independently when a consumer uses api but not sync.\r\n\r\n//? Double-cast + any-spread disabled file-wide: bridges `Rule.Node` to specific\r\n//? AST shapes; the spread iterates over `Object.entries(node)` whose values are\r\n//? typed as `any` because eslint's AST shape varies by node kind.\r\n/* eslint-disable no-restricted-syntax, @typescript-eslint/no-unsafe-argument */\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst SUSPECT_PARAM_NAMES = new Set(['name', 'version', 'route', 'syncName']);\r\nconst SUSPECT_CALLEES = new Set(['syncRequest', 'upsertSyncEventCallback']);\r\n\r\n//? AST keys that point to parents or non-AST metadata. Skipped to avoid\r\n//? infinite loops when eslint adds `parent` pointers during traversal.\r\nconst SKIP_KEYS = new Set(['parent', 'loc', 'range', 'tokens', 'comments']);\r\n\r\nconst containsSuspectCall = (body: unknown): boolean => {\r\n if (!body || typeof body !== 'object') return false;\r\n const visited = new WeakSet<object>();\r\n const stack: unknown[] = [body];\r\n while (stack.length > 0) {\r\n const node = stack.pop() as Record<string, unknown> | null;\r\n if (!node || typeof node !== 'object') continue;\r\n if (visited.has(node)) continue;\r\n visited.add(node);\r\n const callee = (node as { type?: string; callee?: { type?: string; name?: string } }).callee;\r\n if (\r\n (node as { type?: string }).type === 'CallExpression' &&\r\n callee?.type === 'Identifier' &&\r\n SUSPECT_CALLEES.has(callee.name ?? '')\r\n ) {\r\n return true;\r\n }\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) stack.push(...value);\r\n else if (value && typeof value === 'object') stack.push(value);\r\n }\r\n }\r\n return false;\r\n};\r\n\r\nconst isSuspect = (node: {\r\n params?: { type?: string; name?: string; typeAnnotation?: { typeAnnotation?: { type?: string } } }[];\r\n body?: unknown;\r\n}): boolean => {\r\n if (!node.params || node.params.length === 0) return false;\r\n const hasSuspectStringParam = node.params.some((p) => {\r\n if (p.type !== 'Identifier' || !p.name) return false;\r\n if (!SUSPECT_PARAM_NAMES.has(p.name)) return false;\r\n return p.typeAnnotation?.typeAnnotation?.type === 'TSStringKeyword';\r\n });\r\n if (!hasSuspectStringParam) return false;\r\n return containsSuspectCall(node.body);\r\n};\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'problem',\r\n docs: {\r\n description: 'Disallow local wrappers around `syncRequest` / `upsertSyncEventCallback` whose `name`/`version` params are typed as plain `string`.',\r\n },\r\n messages: {\r\n unsafeSyncWrapper:\r\n 'This wrapper around `syncRequest` / `upsertSyncEventCallback` accepts a `string` route param and erases the generic event-payload inference. Call the framework helper directly with literal route names.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n FunctionDeclaration(node) {\r\n if (isSuspect(node)) {\r\n context.report({ node, messageId: 'unsafeSyncWrapper' });\r\n }\r\n },\r\n ArrowFunctionExpression(node) {\r\n if (isSuspect(node)) {\r\n context.report({ node, messageId: 'unsafeSyncWrapper' });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: warn on `confirm(...)` / `window.confirm(...)` calls. The framework\r\n//? provides `menuHandler.confirm({ title, content, input? })` from\r\n//? `src/_functions/menuHandler.ts` which renders a styled, theme-aware,\r\n//? Promise-returning confirm dialog and respects keyboard / accessibility.\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'suggestion',\r\n docs: {\r\n description: 'Prefer `menuHandler.confirm({ title, content })` over the native `confirm()` dialog.',\r\n },\r\n messages: {\r\n useMenuHandlerConfirm:\r\n 'Use `menuHandler.confirm({ title, content })` from `src/_functions/menuHandler.ts` instead of native `confirm()`. Returns a `Promise<boolean>` with proper theming.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n CallExpression(node) {\r\n const callee = node.callee;\r\n if (callee.type === 'Identifier' && callee.name === 'confirm') {\r\n context.report({ node, messageId: 'useMenuHandlerConfirm' });\r\n return;\r\n }\r\n if (\r\n callee.type === 'MemberExpression' &&\r\n callee.object.type === 'Identifier' &&\r\n callee.object.name === 'window' &&\r\n callee.property.type === 'Identifier' &&\r\n callee.property.name === 'confirm'\r\n ) {\r\n context.report({ node, messageId: 'useMenuHandlerConfirm' });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: warn when a JSX `<select>` element is used. The framework ships\r\n//? `Dropdown` (src/_components/Dropdown.tsx) with search, keyboard nav,\r\n//? sizing variants, and consistent dark/light styling. Native `<select>`\r\n//? skips all of that.\r\n\r\nimport type { Rule } from 'eslint';\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\n//? JSX AST nodes are extended at runtime by eslint-plugin-react, but\r\n//? eslint's stock `Rule.RuleListener` type does not know about them. We\r\n//? describe the shapes we read inline.\r\ninterface JsxOpeningElementShape {\r\n name: { type: string; name?: string };\r\n}\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'suggestion',\r\n docs: {\r\n description: 'Prefer the framework `Dropdown` component over native `<select>` elements.',\r\n },\r\n messages: {\r\n useDropdown:\r\n 'Use `Dropdown` from `src/_components/Dropdown.tsx` (or `MultiSelectDropdown` for multi-select) instead of native `<select>`. The framework component handles keyboard nav, search, sizing, and dark/light theme.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n const listener: Rule.RuleListener = {\r\n JSXOpeningElement(rawNode: unknown): void {\r\n const node = rawNode as JsxOpeningElementShape & Rule.Node;\r\n if (node.name.type === 'JSXIdentifier' && node.name.name === 'select') {\r\n context.report({ node, messageId: 'useDropdown' });\r\n }\r\n },\r\n };\r\n return listener;\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Rule: warn on `alert(...)` / `window.alert(...)`. The framework's\r\n//? `notify` helper (from @luckystack/core) renders themed toast\r\n//? notifications via the registered notifier (default sonner).\r\n\r\nimport type { EslintRule } from '../internal/ruleTypes.js';\r\n\r\nconst rule: EslintRule = {\r\n meta: {\r\n type: 'suggestion',\r\n docs: {\r\n description: 'Prefer `notify()` over the native `alert()` dialog.',\r\n },\r\n messages: {\r\n useNotify:\r\n 'Use `notify({ type, message })` from `@luckystack/core` instead of native `alert()`. Renders a themed toast via the registered notifier.',\r\n },\r\n schema: [],\r\n },\r\n create(context) {\r\n return {\r\n CallExpression(node) {\r\n const callee = node.callee;\r\n if (callee.type === 'Identifier' && callee.name === 'alert') {\r\n context.report({ node, messageId: 'useNotify' });\r\n return;\r\n }\r\n if (\r\n callee.type === 'MemberExpression' &&\r\n callee.object.type === 'Identifier' &&\r\n callee.object.name === 'window' &&\r\n callee.property.type === 'Identifier' &&\r\n callee.property.name === 'alert'\r\n ) {\r\n context.report({ node, messageId: 'useNotify' });\r\n }\r\n },\r\n };\r\n },\r\n};\r\n\r\nexport default rule;\r\n","//? Public entry for `@luckystack/core/eslint`. Composes the LuckyStack\r\n//? flat-config tier that the scaffolded `eslint.luckystack.config.js`\r\n//? consumes. Rules that target an optional `@luckystack/*` peer package\r\n//? are gated via `hasPackage(...)` so absent peers cause silent rule\r\n//? skips (NOT noisy \"rule not found\" errors).\r\n//?\r\n//? Override surface: consumers can spread this default export and append\r\n//? their own config blocks, or replace individual rule severities by\r\n//? re-stating them with `'off' | 'warn' | 'error'`.\r\n\r\nimport type { Linter } from 'eslint';\r\n\r\nimport { hasPackage } from './internal/hasPackage.js';\r\nimport type { EslintRule } from './internal/ruleTypes.js';\r\n\r\nimport noArbitraryTailwindColor from './rules/no-arbitrary-tailwind-color.js';\r\nimport noDirectPrismaImportInComponents from './rules/no-direct-prisma-import-in-components.js';\r\nimport noRawFetchInSrc from './rules/no-raw-fetch-in-src.js';\r\nimport noRawTryCatch from './rules/no-raw-try-catch.js';\r\nimport noUnsafeApiWrappers from './rules/no-unsafe-api-wrappers.js';\r\nimport noUnsafeSyncWrappers from './rules/no-unsafe-sync-wrappers.js';\r\nimport preferLuckystackConfirm from './rules/prefer-luckystack-confirm.js';\r\nimport preferLuckystackDropdown from './rules/prefer-luckystack-dropdown.js';\r\nimport preferLuckystackNotify from './rules/prefer-luckystack-notify.js';\r\n\r\nconst allRules: Record<string, EslintRule> = {\r\n 'no-raw-try-catch': noRawTryCatch,\r\n 'no-raw-fetch-in-src': noRawFetchInSrc,\r\n 'no-unsafe-api-wrappers': noUnsafeApiWrappers,\r\n 'no-unsafe-sync-wrappers': noUnsafeSyncWrappers,\r\n 'prefer-luckystack-dropdown': preferLuckystackDropdown,\r\n 'prefer-luckystack-confirm': preferLuckystackConfirm,\r\n 'prefer-luckystack-notify': preferLuckystackNotify,\r\n 'no-direct-prisma-import-in-components': noDirectPrismaImportInComponents,\r\n 'no-arbitrary-tailwind-color': noArbitraryTailwindColor,\r\n};\r\n\r\nconst hasApi = hasPackage('@luckystack/api');\r\nconst hasCore = hasPackage('@luckystack/core');\r\nconst hasSync = hasPackage('@luckystack/sync');\r\n\r\nconst ruleSeverities: Record<string, Linter.RuleSeverity> = {\r\n // Always-on errors.\r\n 'luckystack/no-raw-try-catch': 'error',\r\n\r\n // Package-gated errors. Disabled when the relevant peer is absent so\r\n // a consumer using only @luckystack/core sees no \"use apiRequest\" noise.\r\n 'luckystack/no-raw-fetch-in-src': hasApi || hasCore ? 'error' : 'off',\r\n 'luckystack/no-unsafe-api-wrappers': hasApi ? 'error' : 'off',\r\n 'luckystack/no-unsafe-sync-wrappers': hasSync ? 'error' : 'off',\r\n\r\n // Warnings — enabled by default per framework owner's preference.\r\n 'luckystack/prefer-luckystack-dropdown': 'warn',\r\n 'luckystack/prefer-luckystack-confirm': 'warn',\r\n 'luckystack/prefer-luckystack-notify': 'warn',\r\n 'luckystack/no-direct-prisma-import-in-components': 'warn',\r\n 'luckystack/no-arbitrary-tailwind-color': 'warn',\r\n};\r\n\r\nconst luckystackConfig: Linter.Config[] = [\r\n {\r\n files: ['**/*.{ts,tsx,js,jsx,mjs,cjs}'],\r\n plugins: {\r\n luckystack: { rules: allRules },\r\n },\r\n rules: ruleSeverities,\r\n },\r\n];\r\n\r\nexport default luckystackConfig;\r\n\r\n//? Named exports for advanced consumers who want to assemble their own\r\n//? config or selectively re-enable individual rules.\r\nexport { allRules as rules };\r\n\r\n\r\nexport {hasPackage} from './internal/hasPackage.js';"],"mappings":"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","names":["rule","rule","rule","rule","SUSPECT_PARAM_NAMES","SKIP_KEYS","rule","rule","rule","rule"]}
package/dist/index.d.ts CHANGED
@@ -663,8 +663,21 @@ declare const getEnv: () => RuntimeEnv;
663
663
  declare global {
664
664
  var __luckystackPrisma: PrismaClient | undefined;
665
665
  }
666
+ /**
667
+ * Is a generated `@prisma/client` present in this install? Used by the
668
+ * default resolver's error path and by `/readyz` to decide whether a missing
669
+ * DB ping means "broken" or "this project has no Prisma by design".
670
+ */
671
+ declare const isPrismaClientResolvable: () => boolean;
666
672
  declare const prisma: PrismaClient<_prisma_client.Prisma.PrismaClientOptions, never, _prisma_client_runtime_library.DefaultArgs>;
667
673
 
674
+ type DbHealthResult = boolean | 'skipped';
675
+ type DbHealthCheck = () => Promise<DbHealthResult> | DbHealthResult;
676
+ declare const registerDbHealthCheck: (check: DbHealthCheck) => void;
677
+ declare const getDbHealthCheck: () => DbHealthCheck | null;
678
+ declare const isDbHealthCheckRegistered: () => boolean;
679
+ declare const resetDbHealthCheckForTests: () => void;
680
+
668
681
  interface RedisConnectionOptions {
669
682
  host: string;
670
683
  port: number;
@@ -1876,4 +1889,4 @@ declare const clearAllHooks: () => void;
1876
1889
  declare const registerSyncHook: <TName extends SyncHookName>(name: TName, handler: SyncHookHandler<SyncHookPayloads[TName]>) => (() => void);
1877
1890
  declare const dispatchSyncHook: <TName extends SyncHookName>(name: TName, payload: SyncHookPayloads[TName]) => void;
1878
1891
 
1879
- export { type ApiAuthRejectedPayload, type ApiErrorPayload, type ApiResponseEnvelope, AuthProps, type AvatarConfig, type AvatarConfigInput, BOOT_KEY_PREFIX, BaseSessionLayout, type CheckRateLimitParams, type CookiePrefixConstraints, type CorsRejectedPayload, type CreateLocalizedNormalizerInput, type CsrfConfig, type CsrfCookieOptions, type CsrfMismatchPayload, DEFAULT_AVATAR_CONFIG, DEFAULT_CLIENT_KEY, DEFAULT_CSRF_CONFIG, DEFAULT_ENV_FILES, DEFAULT_REDACTED_LOG_KEYS, DEPTH_TRUNCATED_PLACEHOLDER, DeepPartial, type DeployConfigShape, type DeployDevelopmentShape, type DeployEnvironmentShape, type DeployResourceShape, type DeployRoutingShape, type DispatchResult, type EmailAttachment, type EmailMessage, type EmailResult, type EmailSender, type EmailSenderRegistry, type ErrorFormatter, type ErrorFormatterContext, ErrorParam, ErrorResponseInput, type ErrorTracker, type ErrorTrackerContext, type ErrorTrackerEvent, type ErrorTrackerUser, type FormatterEnvelope, GENERATED_API_DOCS_PATH, GENERATED_API_SCHEMAS_PATH, GENERATED_SOCKET_TYPES_PATH, HealthHashConfig, type HealthHashDescriptor, type HookHandler, type HookName, type HookPayloads, type HookResult, type HookSessionShape, type HookStopSignal, type HttpMethod, type LanguageCode, type LocaleReloader, type LocalizedNormalizer, type NormalizeErrorResponseInput, NormalizedErrorResponse, type OnUploadCompletePayload, type OnUploadStartPayload, type OneTimeTokenHandle, PUBLIC_DIR, type PeerRequire, type PostApiExecutePayload, type PostApiRespondPayload, type PostApiValidatePayload, type PostAvatarServePayload, type PostErrorNormalizePayload, type PostHttpRequestPayload, type PostSessionRefreshPayload, type PostSyncAuthorizePayload, type PostSyncExecutePayload, type PostSyncFanoutPayload, type PostSyncValidatePayload, type PreApiExecutePayload, type PreApiRespondPayload, type PreApiValidatePayload, type PreAvatarServePayload, type PreCaptureFilter, type PreErrorNormalizePayload, type PreHttpRequestPayload, type PreServerStopPayload, type PreSessionRefreshPayload, type PreSocketMessagePayload, type PreSyncAuthorizePayload, type PreSyncExecutePayload, type PreSyncFanoutPayload, type PreSyncRecipientPayload, type PreSyncValidatePayload, type PresetDefinition, type ProcessUploadInput, type ProcessUploadResult, REDACTED_PLACEHOLDER, ROOT_DIR, type RateLimitExceededPayload, type RateLimitResult, type RateLimitStrategy, type RedisConnectionOptions, type RedisKeyFormatter, type Registry, type RegistryOptions, type ResolveClientIpParams, type RoomNameFormatter, type RoomNameFormatterContext, type RuntimeApiMapsResult, type RuntimeEnv, type RuntimeMapsProvider, type RuntimeSyncMapsResult, SERVER_DIR, SERVER_FUNCTIONS_DIR, SHARED_DIR, SRC_DIR, type ServiceDefinition, type ServicesConfigShape, type SessionCreatedPayload, type SessionLogoutInput, type SessionProvider, type SessionRevokedPayload, type SessionSaveResult, type SocketMiddleware, type SpanHandle, type SpanResult, type SyncErrorPayload, type SyncHookHandler, type SyncHookName, type SyncHookPayloads, TSCONFIG_ALIAS_FILES, type TranslateFunction, type TranslateInput, UNKNOWN_CLIENT_IP, UPLOADS_DIR, type ValidationResult$1 as ValidationResult, abortAllForSocket, abortApiByResponseIndex, abortSyncByCb, acquireLease, allowedOrigin, type apiMessage, appendErrorTracker, applyCookiePrefixConstraints, applyErrorFormatter, applySocketMiddlewares, applyStrayKeyPrefix, attachSocketRedisAdapter, bootstrapEnv, captureException, captureExceptionAcrossTrackers, captureMessage, captureMessageAcrossTrackers, checkRateLimit, clearAllHooks, clearAllRateLimits, clearRateLimit, clearSocketMiddlewares, collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, consumeOneTimeToken, consumeOneTimeTokenJson, createLocalizedNormalizer, createRegistry, defaultRateLimitStrategy, defaultRedisKeyFormatter, defaultRoomNameFormatter, deriveTokenBucketId, describeHealthHashConfig, dispatchHook, dispatchSyncHook, ensurePeerDepInstalled, env, escapeHtml, extractLanguageFromHeader, extractTokenFromRequest, extractTokenFromSocket, flushErrorTrackers, formatKey, formatRoomName, getActiveErrorTrackers, getAvatarConfig, getBindAddress, getCookieValue, getCsrfConfig, getCurrentErrorTrackerIdentity, getDeployConfig, getEffectiveHttpMethod, getEmailSender, getEmailSenderByName, getEnv, getEnvFiles, getErrorFormatter, getGeneratedApiDocsPath, getGeneratedApiSchemasPath, getGeneratedSocketTypesPath, getIoInstance, getLocaleReloader, getLocalizedNormalizer, getParams, getPrismaClient, getPrismaClientFor, getPrismaClientKeys, getPublicDir, getRateLimitStatus, getRateLimitStrategy, getRedactedLogKeys, getRedisClient, getRedisClientFor, getRedisClientKeys, getRedisConnectionOptions, getRedisKeyFormatter, getRegisteredSessionProvider, getRoomNameFormatter, getRuntimeApiMaps, getRuntimeSyncMaps, getServerDir, getServerFunctionDirs, getServerFunctionsDir, getServicesConfig, getSharedDir, getSocketMiddlewares, getSrcDir, getUploadsDir, hasCookie, hashSynchronizedValue, hashSynchronizedValueWith, inferHttpMethod, initConsolelog, initSharedSentry, isDeployConfigRegistered, isEmailSenderRegistered, isFalsy, isGetMethodName, isLocalizedNormalizerRegistered, isLoopbackIp, isMethodAllowed, isPrismaClientRegistered, isProduction, isRedactedLogKey, isRedisClientRegistered, isRuntimeMapsProviderRegistered, isServicesConfigRegistered, isSessionProviderRegistered, issueOneTimeToken, listEmailSenderNames, loadEnvFiles, loadPeer, normalizeErrorResponse, normalizeOrigin, oneTimeTokenKey, performLogout, prisma, processUpload, readBootUuid, readSession, recordMetricAcrossTrackers, redis, registerApiAbortController, registerAvatarConfig, registerBindAddress, registerCsrfConfig, registerDeployConfig, registerEmailSender, registerEmailSenders, registerErrorFormatter, registerErrorTracker, registerErrorTrackers, registerHook, registerLocaleReloader, registerLocalizedNormalizer, registerPreCaptureFilter, registerPrismaClient, registerRateLimitStrategy, registerRedactedLogKeys, registerRedisClient, registerRedisKeyFormatter, registerRoomNameFormatter, registerRuntimeMapsProvider, registerServicesConfig, registerSessionProvider, registerSocketMiddleware, registerStrayPrefixCommand, registerSyncAbortController, registerSyncHook, releaseLease, removeSession, renewLease, resetClientsForTests, resetCsrfConfigForTests, resetRedactedLogKeysForTests, resetRedisKeyFormatterForTests, resetSessionProviderForTests, resolveClientIp, resolveEnvKey, resolveErrorMessage, resolveFromRoot, resolveHealthHashConfig, resolveHealthHashConfigFromDescriptor, runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, sanitizeErrorString, sanitizeErrorStrings, sanitizeForLog, serveAvatar, setCurrentErrorTrackerIdentity, setErrorTrackerUser, setIoInstance, setSentryUser, startSpan, startSpanAcrossTrackers, startSpanHandle, type syncMessage, tryCatch, tryCatchSync, unregisterApiAbortController, unregisterSyncAbortController, validateInputByType, validateRequest, writeBootUuid, writeSession };
1892
+ export { type ApiAuthRejectedPayload, type ApiErrorPayload, type ApiResponseEnvelope, AuthProps, type AvatarConfig, type AvatarConfigInput, BOOT_KEY_PREFIX, BaseSessionLayout, type CheckRateLimitParams, type CookiePrefixConstraints, type CorsRejectedPayload, type CreateLocalizedNormalizerInput, type CsrfConfig, type CsrfCookieOptions, type CsrfMismatchPayload, DEFAULT_AVATAR_CONFIG, DEFAULT_CLIENT_KEY, DEFAULT_CSRF_CONFIG, DEFAULT_ENV_FILES, DEFAULT_REDACTED_LOG_KEYS, DEPTH_TRUNCATED_PLACEHOLDER, type DbHealthCheck, type DbHealthResult, DeepPartial, type DeployConfigShape, type DeployDevelopmentShape, type DeployEnvironmentShape, type DeployResourceShape, type DeployRoutingShape, type DispatchResult, type EmailAttachment, type EmailMessage, type EmailResult, type EmailSender, type EmailSenderRegistry, type ErrorFormatter, type ErrorFormatterContext, ErrorParam, ErrorResponseInput, type ErrorTracker, type ErrorTrackerContext, type ErrorTrackerEvent, type ErrorTrackerUser, type FormatterEnvelope, GENERATED_API_DOCS_PATH, GENERATED_API_SCHEMAS_PATH, GENERATED_SOCKET_TYPES_PATH, HealthHashConfig, type HealthHashDescriptor, type HookHandler, type HookName, type HookPayloads, type HookResult, type HookSessionShape, type HookStopSignal, type HttpMethod, type LanguageCode, type LocaleReloader, type LocalizedNormalizer, type NormalizeErrorResponseInput, NormalizedErrorResponse, type OnUploadCompletePayload, type OnUploadStartPayload, type OneTimeTokenHandle, PUBLIC_DIR, type PeerRequire, type PostApiExecutePayload, type PostApiRespondPayload, type PostApiValidatePayload, type PostAvatarServePayload, type PostErrorNormalizePayload, type PostHttpRequestPayload, type PostSessionRefreshPayload, type PostSyncAuthorizePayload, type PostSyncExecutePayload, type PostSyncFanoutPayload, type PostSyncValidatePayload, type PreApiExecutePayload, type PreApiRespondPayload, type PreApiValidatePayload, type PreAvatarServePayload, type PreCaptureFilter, type PreErrorNormalizePayload, type PreHttpRequestPayload, type PreServerStopPayload, type PreSessionRefreshPayload, type PreSocketMessagePayload, type PreSyncAuthorizePayload, type PreSyncExecutePayload, type PreSyncFanoutPayload, type PreSyncRecipientPayload, type PreSyncValidatePayload, type PresetDefinition, type ProcessUploadInput, type ProcessUploadResult, REDACTED_PLACEHOLDER, ROOT_DIR, type RateLimitExceededPayload, type RateLimitResult, type RateLimitStrategy, type RedisConnectionOptions, type RedisKeyFormatter, type Registry, type RegistryOptions, type ResolveClientIpParams, type RoomNameFormatter, type RoomNameFormatterContext, type RuntimeApiMapsResult, type RuntimeEnv, type RuntimeMapsProvider, type RuntimeSyncMapsResult, SERVER_DIR, SERVER_FUNCTIONS_DIR, SHARED_DIR, SRC_DIR, type ServiceDefinition, type ServicesConfigShape, type SessionCreatedPayload, type SessionLogoutInput, type SessionProvider, type SessionRevokedPayload, type SessionSaveResult, type SocketMiddleware, type SpanHandle, type SpanResult, type SyncErrorPayload, type SyncHookHandler, type SyncHookName, type SyncHookPayloads, TSCONFIG_ALIAS_FILES, type TranslateFunction, type TranslateInput, UNKNOWN_CLIENT_IP, UPLOADS_DIR, type ValidationResult$1 as ValidationResult, abortAllForSocket, abortApiByResponseIndex, abortSyncByCb, acquireLease, allowedOrigin, type apiMessage, appendErrorTracker, applyCookiePrefixConstraints, applyErrorFormatter, applySocketMiddlewares, applyStrayKeyPrefix, attachSocketRedisAdapter, bootstrapEnv, captureException, captureExceptionAcrossTrackers, captureMessage, captureMessageAcrossTrackers, checkRateLimit, clearAllHooks, clearAllRateLimits, clearRateLimit, clearSocketMiddlewares, collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, consumeOneTimeToken, consumeOneTimeTokenJson, createLocalizedNormalizer, createRegistry, defaultRateLimitStrategy, defaultRedisKeyFormatter, defaultRoomNameFormatter, deriveTokenBucketId, describeHealthHashConfig, dispatchHook, dispatchSyncHook, ensurePeerDepInstalled, env, escapeHtml, extractLanguageFromHeader, extractTokenFromRequest, extractTokenFromSocket, flushErrorTrackers, formatKey, formatRoomName, getActiveErrorTrackers, getAvatarConfig, getBindAddress, getCookieValue, getCsrfConfig, getCurrentErrorTrackerIdentity, getDbHealthCheck, getDeployConfig, getEffectiveHttpMethod, getEmailSender, getEmailSenderByName, getEnv, getEnvFiles, getErrorFormatter, getGeneratedApiDocsPath, getGeneratedApiSchemasPath, getGeneratedSocketTypesPath, getIoInstance, getLocaleReloader, getLocalizedNormalizer, getParams, getPrismaClient, getPrismaClientFor, getPrismaClientKeys, getPublicDir, getRateLimitStatus, getRateLimitStrategy, getRedactedLogKeys, getRedisClient, getRedisClientFor, getRedisClientKeys, getRedisConnectionOptions, getRedisKeyFormatter, getRegisteredSessionProvider, getRoomNameFormatter, getRuntimeApiMaps, getRuntimeSyncMaps, getServerDir, getServerFunctionDirs, getServerFunctionsDir, getServicesConfig, getSharedDir, getSocketMiddlewares, getSrcDir, getUploadsDir, hasCookie, hashSynchronizedValue, hashSynchronizedValueWith, inferHttpMethod, initConsolelog, initSharedSentry, isDbHealthCheckRegistered, isDeployConfigRegistered, isEmailSenderRegistered, isFalsy, isGetMethodName, isLocalizedNormalizerRegistered, isLoopbackIp, isMethodAllowed, isPrismaClientRegistered, isPrismaClientResolvable, isProduction, isRedactedLogKey, isRedisClientRegistered, isRuntimeMapsProviderRegistered, isServicesConfigRegistered, isSessionProviderRegistered, issueOneTimeToken, listEmailSenderNames, loadEnvFiles, loadPeer, normalizeErrorResponse, normalizeOrigin, oneTimeTokenKey, performLogout, prisma, processUpload, readBootUuid, readSession, recordMetricAcrossTrackers, redis, registerApiAbortController, registerAvatarConfig, registerBindAddress, registerCsrfConfig, registerDbHealthCheck, registerDeployConfig, registerEmailSender, registerEmailSenders, registerErrorFormatter, registerErrorTracker, registerErrorTrackers, registerHook, registerLocaleReloader, registerLocalizedNormalizer, registerPreCaptureFilter, registerPrismaClient, registerRateLimitStrategy, registerRedactedLogKeys, registerRedisClient, registerRedisKeyFormatter, registerRoomNameFormatter, registerRuntimeMapsProvider, registerServicesConfig, registerSessionProvider, registerSocketMiddleware, registerStrayPrefixCommand, registerSyncAbortController, registerSyncHook, releaseLease, removeSession, renewLease, resetClientsForTests, resetCsrfConfigForTests, resetDbHealthCheckForTests, resetRedactedLogKeysForTests, resetRedisKeyFormatterForTests, resetSessionProviderForTests, resolveClientIp, resolveEnvKey, resolveErrorMessage, resolveFromRoot, resolveHealthHashConfig, resolveHealthHashConfigFromDescriptor, runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, sanitizeErrorString, sanitizeErrorStrings, sanitizeForLog, serveAvatar, setCurrentErrorTrackerIdentity, setErrorTrackerUser, setIoInstance, setSentryUser, startSpan, startSpanAcrossTrackers, startSpanHandle, type syncMessage, tryCatch, tryCatchSync, unregisterApiAbortController, unregisterSyncAbortController, validateInputByType, validateRequest, writeBootUuid, writeSession };
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ import {
49
49
  tryCatchSync,
50
50
  validatePagePath,
51
51
  waitForSocket
52
- } from "./chunk-H64EC7Q6.js";
52
+ } from "./chunk-A3VMC576.js";
53
53
  import {
54
54
  DEFAULT_PROJECT_CONFIG,
55
55
  deepMerge,
@@ -58,7 +58,7 @@ import {
58
58
  isPlainObject,
59
59
  isProjectConfigRegistered,
60
60
  registerProjectConfig
61
- } from "./chunk-6BBJ2Q6I.js";
61
+ } from "./chunk-ECWDUWBG.js";
62
62
  import {
63
63
  DEFAULT_REDACTED_LOG_KEYS,
64
64
  DEPTH_TRUNCATED_PLACEHOLDER,
@@ -96,7 +96,7 @@ import {
96
96
  startSpan,
97
97
  startSpanAcrossTrackers,
98
98
  startSpanHandle
99
- } from "./chunk-TGYBUT4N.js";
99
+ } from "./chunk-ELHTLIEX.js";
100
100
  import {
101
101
  createRegistry
102
102
  } from "./chunk-USQA6QTX.js";
@@ -476,7 +476,7 @@ var isProduction = env.NODE_ENV === "production";
476
476
  var getEnv = () => bootstrapEnv();
477
477
 
478
478
  // src/db.ts
479
- import { PrismaClient } from "@prisma/client";
479
+ import { createRequire as createRequire2 } from "module";
480
480
 
481
481
  // src/clients.ts
482
482
  var DEFAULT_CLIENT_KEY = "default";
@@ -526,8 +526,24 @@ var resetClientsForTests = () => {
526
526
  };
527
527
 
528
528
  // src/db.ts
529
+ var requireFromCore = createRequire2(import.meta.url);
530
+ var isPrismaClientResolvable = () => {
531
+ const [error] = tryCatchSync(() => requireFromCore.resolve("@prisma/client"));
532
+ return !error;
533
+ };
529
534
  var buildDefaultPrismaClient = () => {
530
- globalThis.__luckystackPrisma ??= new PrismaClient();
535
+ if (!globalThis.__luckystackPrisma) {
536
+ const [loadError, loaded] = tryCatchSync(
537
+ () => requireFromCore("@prisma/client")
538
+ );
539
+ const PrismaClientCtor = loaded?.PrismaClient;
540
+ if (loadError || typeof PrismaClientCtor !== "function") {
541
+ throw new Error(
542
+ "[luckystack] no database client is available: '@prisma/client' is not installed/generated and no client was registered. Register your own database client in `luckystack/core/clients.ts` via registerPrismaClient(...) (any client whose surface your handlers use) \u2014 and swap the login UserAdapter via registerUserAdapter(...) if auth is on \u2014 or install Prisma: `npm i @prisma/client prisma` + `npx prisma generate`. Projects scaffolded with orm: 'none' are expected to fill in that file."
543
+ );
544
+ }
545
+ globalThis.__luckystackPrisma = new PrismaClientCtor();
546
+ }
531
547
  return globalThis.__luckystackPrisma;
532
548
  };
533
549
  setDefaultPrismaResolver(buildDefaultPrismaClient);
@@ -553,6 +569,17 @@ var prisma = new Proxy({}, {
553
569
  getOwnPropertyDescriptor: (_target, prop) => Reflect.getOwnPropertyDescriptor(getPrismaClient(), prop)
554
570
  });
555
571
 
572
+ // src/dbHealthCheck.ts
573
+ var activeCheck = null;
574
+ var registerDbHealthCheck = (check) => {
575
+ activeCheck = check;
576
+ };
577
+ var getDbHealthCheck = () => activeCheck;
578
+ var isDbHealthCheckRegistered = () => activeCheck !== null;
579
+ var resetDbHealthCheckForTests = () => {
580
+ activeCheck = null;
581
+ };
582
+
556
583
  // src/redis.ts
557
584
  import Redis from "ioredis";
558
585
 
@@ -1887,7 +1914,7 @@ var validateInputByType = async ({
1887
1914
  return { status: "success" };
1888
1915
  }
1889
1916
  if (resolveEnvKey() === "production") {
1890
- const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-FITPOAMC.js");
1917
+ const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-P2AHHQVW.js");
1891
1918
  if (getProjectConfig2().validation.runtimeMode === "off") {
1892
1919
  return { status: "success" };
1893
1920
  }
@@ -2138,6 +2165,7 @@ export {
2138
2165
  getCsrfConfig,
2139
2166
  getCsrfToken,
2140
2167
  getCurrentErrorTrackerIdentity,
2168
+ getDbHealthCheck,
2141
2169
  getDeployConfig,
2142
2170
  getEffectiveHttpMethod,
2143
2171
  getEmailSender,
@@ -2191,6 +2219,7 @@ export {
2191
2219
  initConsolelog,
2192
2220
  initSharedSentry,
2193
2221
  isApiMethodMapRegistered,
2222
+ isDbHealthCheckRegistered,
2194
2223
  isDeployConfigRegistered,
2195
2224
  isEmailSenderRegistered,
2196
2225
  isErrorParamArray,
@@ -2203,6 +2232,7 @@ export {
2203
2232
  isOnline,
2204
2233
  isPlainObject,
2205
2234
  isPrismaClientRegistered,
2235
+ isPrismaClientResolvable,
2206
2236
  isProduction,
2207
2237
  isProjectConfigRegistered,
2208
2238
  isRedactedLogKey,
@@ -2233,6 +2263,7 @@ export {
2233
2263
  registerAvatarConfig,
2234
2264
  registerBindAddress,
2235
2265
  registerCsrfConfig,
2266
+ registerDbHealthCheck,
2236
2267
  registerDeployConfig,
2237
2268
  registerEmailSender,
2238
2269
  registerEmailSenders,
@@ -2267,6 +2298,7 @@ export {
2267
2298
  renewLease,
2268
2299
  resetClientsForTests,
2269
2300
  resetCsrfConfigForTests,
2301
+ resetDbHealthCheckForTests,
2270
2302
  resetLoggerForTests,
2271
2303
  resetRedactedLogKeysForTests,
2272
2304
  resetRedisKeyFormatterForTests,