@ankhorage/infra 0.3.2 → 0.3.4
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/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/dist/adapters/minikube/auth/supabase/index.d.ts.map +1 -1
- package/dist/adapters/minikube/auth/supabase/index.js +14 -163
- package/dist/adapters/minikube/auth/supabase/index.js.map +1 -1
- package/dist/adapters/minikube/auth/supabase/profile.d.ts +21 -0
- package/dist/adapters/minikube/auth/supabase/profile.d.ts.map +1 -0
- package/dist/adapters/minikube/auth/supabase/profile.js +452 -0
- package/dist/adapters/minikube/auth/supabase/profile.js.map +1 -0
- package/dist/adapters/minikube/authz/cerbos/index.d.ts +1 -1
- package/dist/adapters/minikube/authz/cerbos/index.d.ts.map +1 -1
- package/dist/adapters/minikube/authz/cerbos/index.js +5 -3
- package/dist/adapters/minikube/authz/cerbos/index.js.map +1 -1
- package/dist/adapters/minikube/authz/index.d.ts +1 -1
- package/dist/adapters/minikube/authz/index.d.ts.map +1 -1
- package/dist/adapters/minikube/authz/index.js +1 -1
- package/dist/adapters/minikube/authz/index.js.map +1 -1
- package/dist/adapters/minikube/base/index.d.ts.map +1 -1
- package/dist/adapters/minikube/base/index.js +850 -54
- package/dist/adapters/minikube/base/index.js.map +1 -1
- package/dist/adapters/minikube/index.js +2 -1
- package/dist/adapters/minikube/index.js.map +1 -1
- package/dist/infraValidation.js +1 -1
- package/dist/infraValidation.js.map +1 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +1 -18
- package/dist/project.js.map +1 -1
- package/dist/testSupport.d.ts.map +1 -1
- package/dist/testSupport.js +0 -6
- package/dist/testSupport.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/minikube/auth/supabase/index.test.ts +152 -18
- package/src/adapters/minikube/auth/supabase/index.ts +18 -205
- package/src/adapters/minikube/auth/supabase/profile.integration.test.ts +860 -0
- package/src/adapters/minikube/auth/supabase/profile.ts +526 -0
- package/src/adapters/minikube/authz/cerbos/index.ts +7 -5
- package/src/adapters/minikube/authz/index.ts +2 -2
- package/src/adapters/minikube/base/index.test.ts +100 -6
- package/src/adapters/minikube/base/index.ts +880 -54
- package/src/adapters/minikube/index.ts +3 -1
- package/src/index.test.ts +13 -2
- package/src/infraValidation.ts +1 -1
- package/src/project.ts +2 -22
- package/src/testSupport.ts +0 -6
- package/src/types.ts +1 -1
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest, AuthFlowConfig, NavigatorSpec } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nexport interface ResolvedInfraProject {\n readonly appsRoot: string;\n readonly manifest: AppManifest;\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly workspaceRoot: string;\n}\n\nexport async function resolveInfraProject(options: {\n readonly cwd: string;\n readonly projectId?: string;\n}): Promise<ResolvedInfraProject> {\n const workspaceRoot = await findWorkspaceRoot(options.cwd);\n if (workspaceRoot === null) {\n throw new Error(`Could not find an Ankh workspace root from cwd: ${options.cwd}`);\n }\n\n const appsRoot = path.join(workspaceRoot, 'apps');\n const projectId =\n options.projectId === undefined\n ? inferProjectId(options.cwd, appsRoot)\n : validateExplicitProjectId(options.projectId);\n if (projectId === null) {\n throw new Error(\n 'Could not infer a project from cwd. Pass a project id or run the command inside apps/<project>.',\n );\n }\n\n const projectPath = resolveProjectPath(appsRoot, projectId);\n if (!(await pathExists(projectPath))) {\n throw new Error(`Project '${projectId}' not found.`);\n }\n\n const manifestPath = path.join(projectPath, 'ankh.config.json');\n const manifest = await readProjectManifest({\n manifestPath,\n projectId,\n projectPath,\n });\n\n return {\n appsRoot,\n manifest,\n manifestPath,\n projectId,\n projectPath,\n workspaceRoot,\n };\n}\n\nfunction resolveProjectPath(appsRoot: string, projectId: string): string {\n const projectPath = path.resolve(appsRoot, projectId);\n const appsRootPath = `${path.resolve(appsRoot)}${path.sep}`;\n\n if (!projectPath.startsWith(appsRootPath)) {\n throw new Error('Security check failed while resolving the project path.');\n }\n\n return projectPath;\n}\n\nfunction createFallbackManifest(projectId: string): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '0.0.0',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra: {\n plugins: [],\n },\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n authFlow: {\n signInRoute: '/sign-in',\n signOutRoute: '/sign-out',\n postSignInRoute: '/',\n unauthorizedRoute: '/sign-in',\n },\n },\n };\n}\n\nasync function readProjectManifest(args: {\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n}): Promise<AppManifest> {\n if (!(await pathExists(args.manifestPath))) {\n return createFallbackManifest(args.projectId);\n }\n\n const rawManifest = await fs.readFile(args.manifestPath, 'utf8');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawManifest);\n } catch (error) {\n throw new Error(\n `Project manifest is not valid JSON: ${args.manifestPath}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n }\n\n if (!isAppManifest(parsed)) {\n throw new Error(`Project manifest has an invalid shape: ${args.manifestPath}`);\n }\n\n return parsed;\n}\n\nasync function findWorkspaceRoot(startPath: string): Promise<string | null> {\n let currentPath = path.resolve(startPath);\n\n for (;;) {\n const appsRoot = path.join(currentPath, 'apps');\n if (await isDirectory(appsRoot)) {\n return currentPath;\n }\n\n const parentPath = path.dirname(currentPath);\n if (parentPath === currentPath) {\n return null;\n }\n\n currentPath = parentPath;\n }\n}\n\nfunction inferProjectId(cwd: string, appsRoot: string): string | null {\n const relativePath = path.relative(path.resolve(appsRoot), path.resolve(cwd));\n if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n return null;\n }\n\n const [projectId] = relativePath.split(path.sep);\n return projectId === undefined || projectId.length === 0 ? null : path.basename(projectId);\n}\n\nfunction validateExplicitProjectId(projectId: string): string {\n if (projectId.trim().length === 0) {\n throw new Error('Invalid project id: expected a single safe project segment.');\n }\n\n if (\n projectId === '.' ||\n projectId === '..' ||\n path.isAbsolute(projectId) ||\n projectId.includes('/') ||\n projectId.includes('\\\\')\n ) {\n throw new Error(`Invalid project id: ${projectId}. Expected a single safe project segment.`);\n }\n\n return projectId;\n}\n\nfunction isAppManifest(value: unknown): value is AppManifest {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n isAppManifestMetadata(value.metadata) &&\n Array.isArray(value.themes) &&\n typeof value.activeThemeId === 'string' &&\n isInfraManifestRecord(value.infra) &&\n isNavigatorSpec(value.navigator) &&\n isRecord(value.screens) &&\n isSettingsRecord(value.settings)\n );\n}\n\nfunction isAppManifestMetadata(value: unknown): value is AppManifest['metadata'] {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n typeof value.slug === 'string' &&\n typeof value.version === 'string' &&\n typeof value.themeId === 'string' &&\n (value.created === undefined || typeof value.created === 'string') &&\n (value.updated === undefined || typeof value.updated === 'string')\n );\n}\n\nfunction isInfraManifestRecord(value: unknown): value is AppManifest['infra'] {\n return isRecord(value) && Array.isArray(value.plugins);\n}\n\nfunction isNavigatorSpec(value: unknown): value is NavigatorSpec {\n return (\n isRecord(value) &&\n typeof value.type === 'string' &&\n Array.isArray(value.routes) &&\n (value.initialRouteName === undefined || typeof value.initialRouteName === 'string')\n );\n}\n\nfunction isSettingsRecord(value: unknown): value is AppManifest['settings'] {\n return (\n isRecord(value) &&\n (value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&\n isLocalizationRecord(value.localization) &&\n isAuthFlowConfig(value.authFlow)\n );\n}\n\nfunction isLocalizationRecord(value: unknown): value is AppManifest['settings']['localization'] {\n return (\n isRecord(value) &&\n typeof value.defaultLocale === 'string' &&\n Array.isArray(value.locales) &&\n value.locales.every((locale) => typeof locale === 'string')\n );\n}\n\nfunction isAuthFlowConfig(value: unknown): value is AuthFlowConfig {\n return (\n isRecord(value) &&\n typeof value.signInRoute === 'string' &&\n typeof value.postSignInRoute === 'string' &&\n (value.signUpRoute === undefined || typeof value.signUpRoute === 'string') &&\n (value.signOutRoute === undefined || typeof value.signOutRoute === 'string') &&\n (value.forgotPasswordRoute === undefined || typeof value.forgotPasswordRoute === 'string') &&\n (value.otpRoute === undefined || typeof value.otpRoute === 'string') &&\n (value.unauthorizedRoute === undefined || typeof value.unauthorizedRoute === 'string')\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nasync function isDirectory(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest, NavigatorSpec } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nexport interface ResolvedInfraProject {\n readonly appsRoot: string;\n readonly manifest: AppManifest;\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly workspaceRoot: string;\n}\n\nexport async function resolveInfraProject(options: {\n readonly cwd: string;\n readonly projectId?: string;\n}): Promise<ResolvedInfraProject> {\n const workspaceRoot = await findWorkspaceRoot(options.cwd);\n if (workspaceRoot === null) {\n throw new Error(`Could not find an Ankh workspace root from cwd: ${options.cwd}`);\n }\n\n const appsRoot = path.join(workspaceRoot, 'apps');\n const projectId =\n options.projectId === undefined\n ? inferProjectId(options.cwd, appsRoot)\n : validateExplicitProjectId(options.projectId);\n if (projectId === null) {\n throw new Error(\n 'Could not infer a project from cwd. Pass a project id or run the command inside apps/<project>.',\n );\n }\n\n const projectPath = resolveProjectPath(appsRoot, projectId);\n if (!(await pathExists(projectPath))) {\n throw new Error(`Project '${projectId}' not found.`);\n }\n\n const manifestPath = path.join(projectPath, 'ankh.config.json');\n const manifest = await readProjectManifest({\n manifestPath,\n projectId,\n projectPath,\n });\n\n return {\n appsRoot,\n manifest,\n manifestPath,\n projectId,\n projectPath,\n workspaceRoot,\n };\n}\n\nfunction resolveProjectPath(appsRoot: string, projectId: string): string {\n const projectPath = path.resolve(appsRoot, projectId);\n const appsRootPath = `${path.resolve(appsRoot)}${path.sep}`;\n\n if (!projectPath.startsWith(appsRootPath)) {\n throw new Error('Security check failed while resolving the project path.');\n }\n\n return projectPath;\n}\n\nfunction createFallbackManifest(projectId: string): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '0.0.0',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra: {\n plugins: [],\n },\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n\nasync function readProjectManifest(args: {\n readonly manifestPath: string;\n readonly projectId: string;\n readonly projectPath: string;\n}): Promise<AppManifest> {\n if (!(await pathExists(args.manifestPath))) {\n return createFallbackManifest(args.projectId);\n }\n\n const rawManifest = await fs.readFile(args.manifestPath, 'utf8');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(rawManifest);\n } catch (error) {\n throw new Error(\n `Project manifest is not valid JSON: ${args.manifestPath}: ${getErrorMessage(error)}`,\n { cause: error },\n );\n }\n\n if (!isAppManifest(parsed)) {\n throw new Error(`Project manifest has an invalid shape: ${args.manifestPath}`);\n }\n\n return parsed;\n}\n\nasync function findWorkspaceRoot(startPath: string): Promise<string | null> {\n let currentPath = path.resolve(startPath);\n\n for (;;) {\n const appsRoot = path.join(currentPath, 'apps');\n if (await isDirectory(appsRoot)) {\n return currentPath;\n }\n\n const parentPath = path.dirname(currentPath);\n if (parentPath === currentPath) {\n return null;\n }\n\n currentPath = parentPath;\n }\n}\n\nfunction inferProjectId(cwd: string, appsRoot: string): string | null {\n const relativePath = path.relative(path.resolve(appsRoot), path.resolve(cwd));\n if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n return null;\n }\n\n const [projectId] = relativePath.split(path.sep);\n return projectId === undefined || projectId.length === 0 ? null : path.basename(projectId);\n}\n\nfunction validateExplicitProjectId(projectId: string): string {\n if (projectId.trim().length === 0) {\n throw new Error('Invalid project id: expected a single safe project segment.');\n }\n\n if (\n projectId === '.' ||\n projectId === '..' ||\n path.isAbsolute(projectId) ||\n projectId.includes('/') ||\n projectId.includes('\\\\')\n ) {\n throw new Error(`Invalid project id: ${projectId}. Expected a single safe project segment.`);\n }\n\n return projectId;\n}\n\nfunction isAppManifest(value: unknown): value is AppManifest {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n isAppManifestMetadata(value.metadata) &&\n Array.isArray(value.themes) &&\n typeof value.activeThemeId === 'string' &&\n isInfraManifestRecord(value.infra) &&\n isNavigatorSpec(value.navigator) &&\n isRecord(value.screens) &&\n isSettingsRecord(value.settings)\n );\n}\n\nfunction isAppManifestMetadata(value: unknown): value is AppManifest['metadata'] {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n typeof value.slug === 'string' &&\n typeof value.version === 'string' &&\n typeof value.themeId === 'string' &&\n (value.created === undefined || typeof value.created === 'string') &&\n (value.updated === undefined || typeof value.updated === 'string')\n );\n}\n\nfunction isInfraManifestRecord(value: unknown): value is AppManifest['infra'] {\n return isRecord(value) && Array.isArray(value.plugins);\n}\n\nfunction isNavigatorSpec(value: unknown): value is NavigatorSpec {\n return (\n isRecord(value) &&\n typeof value.type === 'string' &&\n Array.isArray(value.routes) &&\n (value.initialRouteName === undefined || typeof value.initialRouteName === 'string')\n );\n}\n\nfunction isSettingsRecord(value: unknown): value is AppManifest['settings'] {\n return (\n isRecord(value) &&\n (value.apiBaseUrl === undefined || typeof value.apiBaseUrl === 'string') &&\n isLocalizationRecord(value.localization)\n );\n}\n\nfunction isLocalizationRecord(value: unknown): value is AppManifest['settings']['localization'] {\n return (\n isRecord(value) &&\n typeof value.defaultLocale === 'string' &&\n Array.isArray(value.locales) &&\n value.locales.every((locale) => typeof locale === 'string')\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nasync function isDirectory(filePath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(filePath);\n return stat.isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testSupport.d.ts","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,SAAU,GAChB,sBAAsB,CAmBxB;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,GAAE;IACJ,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CACxB,GACL,OAAO,CAAC;IACT,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC,CA6BD;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,WAAW,CAAC,OAAO,CAAmB,GAC5C,WAAW,
|
|
1
|
+
{"version":3,"file":"testSupport.d.ts","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,SAAU,GAChB,sBAAsB,CAmBxB;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,GAAE;IACJ,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CACxB,GACL,OAAO,CAAC;IACT,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC,CA6BD;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,WAAW,CAAC,OAAO,CAAmB,GAC5C,WAAW,CAuBb"}
|
package/dist/testSupport.js
CHANGED
|
@@ -61,12 +61,6 @@ export function createAppManifest(projectId, infra = { plugins: [] }) {
|
|
|
61
61
|
defaultLocale: 'en',
|
|
62
62
|
locales: ['en'],
|
|
63
63
|
},
|
|
64
|
-
authFlow: {
|
|
65
|
-
signInRoute: '/sign-in',
|
|
66
|
-
signOutRoute: '/sign-out',
|
|
67
|
-
postSignInRoute: '/',
|
|
68
|
-
unauthorizedRoute: '/sign-in',
|
|
69
|
-
},
|
|
70
64
|
},
|
|
71
65
|
};
|
|
72
66
|
}
|
package/dist/testSupport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testSupport.js","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"testSupport.js","sourceRoot":"","sources":["../src/testSupport.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppManifest } from '@ankhorage/contracts';\nimport { promises as fs } from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport type { InfraCommandContext } from './commandContext.js';\n\nexport interface CapturedCommandContext {\n readonly context: InfraCommandContext;\n readonly stderr: { value: string };\n readonly stdout: { value: string };\n}\n\nexport function createCapturedCommandContext(\n cwd: string,\n version = '0.2.1',\n): CapturedCommandContext {\n const stdout = { value: '' };\n const stderr = { value: '' };\n\n return {\n stdout,\n stderr,\n context: {\n cwd,\n env: process.env,\n version,\n writeStdout(text: string) {\n stdout.value += text;\n },\n writeStderr(text: string) {\n stderr.value += text;\n },\n },\n };\n}\n\nexport async function createWorkspaceFixture(\n args: {\n readonly manifest?: AppManifest;\n readonly manifestFile?: 'invalid-json' | 'missing';\n readonly projectId?: string;\n } = {},\n): Promise<{\n readonly appsRoot: string;\n readonly projectId: string;\n readonly projectPath: string;\n readonly rootPath: string;\n}> {\n const rootPath = await fs.mkdtemp(path.join(os.tmpdir(), 'ankhorage-infra-'));\n const appsRoot = path.join(rootPath, 'apps');\n const projectId = args.projectId ?? 'shop';\n const projectPath = path.join(appsRoot, projectId);\n\n await fs.mkdir(projectPath, { recursive: true });\n await fs.writeFile(\n path.join(projectPath, 'package.json'),\n JSON.stringify({ name: projectId, version: '1.0.0' }, null, 2),\n 'utf8',\n );\n\n if (args.manifestFile === 'invalid-json') {\n await fs.writeFile(path.join(projectPath, 'ankh.config.json'), '{invalid', 'utf8');\n } else if (args.manifestFile !== 'missing') {\n await fs.writeFile(\n path.join(projectPath, 'ankh.config.json'),\n `${JSON.stringify(args.manifest ?? createAppManifest(projectId), null, 2)}\\n`,\n 'utf8',\n );\n }\n\n return {\n appsRoot,\n projectId,\n projectPath,\n rootPath,\n };\n}\n\nexport function createAppManifest(\n projectId: string,\n infra: AppManifest['infra'] = { plugins: [] },\n): AppManifest {\n return {\n metadata: {\n name: projectId,\n slug: projectId,\n version: '1.0.0',\n themeId: 'default',\n },\n themes: [],\n activeThemeId: 'default',\n infra,\n navigator: {\n type: 'stack',\n routes: [{ name: 'index', screenId: 'index' }],\n },\n screens: {},\n settings: {\n localization: {\n defaultLocale: 'en',\n locales: ['en'],\n },\n },\n };\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface InfrastructureGenerationOptions {
|
|
|
30
30
|
/**
|
|
31
31
|
* Optional app-level context used by provider/engine-specific generators.
|
|
32
32
|
*/
|
|
33
|
-
readonly appManifest?: Pick<AppManifest, 'data' | 'metadata' | 'navigator' | 'screens' | 'settings'>;
|
|
33
|
+
readonly appManifest?: Pick<AppManifest, 'data' | 'infra' | 'metadata' | 'navigator' | 'screens' | 'settings'>;
|
|
34
34
|
}
|
|
35
35
|
export interface InfraGenerationInput {
|
|
36
36
|
/**
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEvE,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACpE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,2BAA2B,EAAE,CAAC;IACvD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,SAAS,0BAA0B,EAAE,CAAC;CAC9D;AAED,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CACzB,WAAW,EACX,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEvE,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACpE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,2BAA2B,EAAE,CAAC;IACvD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,SAAS,0BAA0B,EAAE,CAAC;CAC9D;AAED,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CACzB,WAAW,EACX,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CACrE,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,+BAA+B,CAAC;CACpD;AAED,MAAM,MAAM,sBAAsB,GAAG,+BAA+B,CAAC;AACrE,MAAM,MAAM,qBAAqB,GAAG,8BAA8B,CAAC;AACnE,MAAM,MAAM,aAAa,GAAG,2BAA2B,CAAC;AACxD,MAAM,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,KAAK,oBAAoB,GAAG,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC;AAE3F,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;CAC1C,CAAC"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AppManifest, InfraManifest } from '@ankhorage/contracts';\n\nexport interface GeneratedInfrastructureFile {\n /**\n * Path relative to the target app root.\n */\n readonly path: string;\n readonly content: string;\n readonly executable?: boolean;\n}\n\nexport interface GeneratedPackageDependency {\n readonly name: string;\n readonly version: string;\n readonly reason: string;\n}\n\nexport interface InfrastructureGenerationMeta {\n readonly target: NonNullable<InfraManifest['deployment']>['target'];\n readonly providers: readonly string[];\n}\n\nexport interface InfrastructureGenerationResult {\n readonly files: readonly GeneratedInfrastructureFile[];\n readonly warnings: readonly string[];\n readonly meta: InfrastructureGenerationMeta;\n readonly dependencies: readonly GeneratedPackageDependency[];\n}\n\nexport interface InfrastructureGenerationOptions {\n /**\n * Optional namespace hint from caller context, such as app slug or project id.\n */\n readonly namespaceHint?: string;\n /**\n * Optional app-level context used by provider/engine-specific generators.\n */\n readonly appManifest?: Pick<\n AppManifest,\n 'data' | 'metadata' | 'navigator' | 'screens' | 'settings'\n >;\n}\n\nexport interface InfraGenerationInput {\n /**\n * Infra manifest section from an app manifest.\n */\n readonly manifest: InfraManifestInput;\n /**\n * Optional root used by callers that want to keep output intent alongside generation.\n * The generator itself still returns relative file paths and does not write to disk.\n */\n readonly outputRoot?: string;\n readonly options?: InfrastructureGenerationOptions;\n}\n\nexport type InfraGenerationOptions = InfrastructureGenerationOptions;\nexport type InfraGenerationResult = InfrastructureGenerationResult;\nexport type GeneratedFile = GeneratedInfrastructureFile;\nexport type PackageDependency = GeneratedPackageDependency;\n\nexport interface InfraDiagnostic {\n readonly severity: 'warning';\n readonly message: string;\n}\n\ntype InfraStorageProvider = NonNullable<InfraManifest['storage']>['provider'] | 'supabase';\n\ninterface InfraStorageSpecInput {\n readonly provider: InfraStorageProvider;\n readonly buckets: readonly string[];\n}\n\n/**\n * Infra-local manifest input type.\n *\n * This is intentionally broader than `@ankhorage/contracts` to allow adapters to support\n * additional providers before the shared contracts package officially includes them.\n */\nexport type InfraManifestInput = Omit<InfraManifest, 'storage'> & {\n readonly storage?: InfraStorageSpecInput;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { AppManifest, InfraManifest } from '@ankhorage/contracts';\n\nexport interface GeneratedInfrastructureFile {\n /**\n * Path relative to the target app root.\n */\n readonly path: string;\n readonly content: string;\n readonly executable?: boolean;\n}\n\nexport interface GeneratedPackageDependency {\n readonly name: string;\n readonly version: string;\n readonly reason: string;\n}\n\nexport interface InfrastructureGenerationMeta {\n readonly target: NonNullable<InfraManifest['deployment']>['target'];\n readonly providers: readonly string[];\n}\n\nexport interface InfrastructureGenerationResult {\n readonly files: readonly GeneratedInfrastructureFile[];\n readonly warnings: readonly string[];\n readonly meta: InfrastructureGenerationMeta;\n readonly dependencies: readonly GeneratedPackageDependency[];\n}\n\nexport interface InfrastructureGenerationOptions {\n /**\n * Optional namespace hint from caller context, such as app slug or project id.\n */\n readonly namespaceHint?: string;\n /**\n * Optional app-level context used by provider/engine-specific generators.\n */\n readonly appManifest?: Pick<\n AppManifest,\n 'data' | 'infra' | 'metadata' | 'navigator' | 'screens' | 'settings'\n >;\n}\n\nexport interface InfraGenerationInput {\n /**\n * Infra manifest section from an app manifest.\n */\n readonly manifest: InfraManifestInput;\n /**\n * Optional root used by callers that want to keep output intent alongside generation.\n * The generator itself still returns relative file paths and does not write to disk.\n */\n readonly outputRoot?: string;\n readonly options?: InfrastructureGenerationOptions;\n}\n\nexport type InfraGenerationOptions = InfrastructureGenerationOptions;\nexport type InfraGenerationResult = InfrastructureGenerationResult;\nexport type GeneratedFile = GeneratedInfrastructureFile;\nexport type PackageDependency = GeneratedPackageDependency;\n\nexport interface InfraDiagnostic {\n readonly severity: 'warning';\n readonly message: string;\n}\n\ntype InfraStorageProvider = NonNullable<InfraManifest['storage']>['provider'] | 'supabase';\n\ninterface InfraStorageSpecInput {\n readonly provider: InfraStorageProvider;\n readonly buckets: readonly string[];\n}\n\n/**\n * Infra-local manifest input type.\n *\n * This is intentionally broader than `@ankhorage/contracts` to allow adapters to support\n * additional providers before the shared contracts package officially includes them.\n */\nexport type InfraManifestInput = Omit<InfraManifest, 'storage'> & {\n readonly storage?: InfraStorageSpecInput;\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/infra",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.4",
|
|
5
5
|
"description": "Project-aware infrastructure generation library and executable Ankh provider for Ankhorage.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/infra#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"openapi"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ankhorage/contracts": "^
|
|
36
|
+
"@ankhorage/contracts": "^2.0.0"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
@@ -27,35 +27,67 @@ describe('generateSupabaseAuthArtifacts profile tables', () => {
|
|
|
27
27
|
expect(runtimeConfig?.content).toContain('AUTH_PROFILE_CREATE_STRATEGY: "trigger"');
|
|
28
28
|
expect(runtimeConfig?.content).toContain('AUTH_PROFILE_UPDATE_STRATEGY: "api"');
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
(file) => file.path === 'infra/minikube/supabase/
|
|
30
|
+
const reconciliation = artifacts.files.find(
|
|
31
|
+
(file) => file.path === 'infra/minikube/supabase/generated/auth_profiles.sql',
|
|
32
32
|
);
|
|
33
|
-
expect(
|
|
34
|
-
expect(
|
|
33
|
+
expect(reconciliation?.content).toContain('begin;');
|
|
34
|
+
expect(reconciliation?.content).toContain('commit;');
|
|
35
|
+
expect(reconciliation?.content).toContain('create table if not exists public."profiles"');
|
|
36
|
+
expect(reconciliation?.content).toContain(
|
|
35
37
|
'id uuid primary key references auth.users(id) on delete cascade',
|
|
36
38
|
);
|
|
37
|
-
expect(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expect(
|
|
39
|
+
expect(reconciliation?.content).toContain(
|
|
40
|
+
'alter table public."profiles" add column if not exists "display_name" text',
|
|
41
|
+
);
|
|
42
|
+
expect(reconciliation?.content).toContain(
|
|
43
|
+
'alter table public."profiles" add column if not exists "avatar_url" text',
|
|
44
|
+
);
|
|
45
|
+
expect(reconciliation?.content).toContain(
|
|
46
|
+
'alter table public."profiles" drop column if exists role',
|
|
47
|
+
);
|
|
48
|
+
expect(reconciliation?.content).toContain(
|
|
49
|
+
'alter table public."profiles" drop column if exists "first_name"',
|
|
50
|
+
);
|
|
51
|
+
expect(reconciliation?.content).toContain(
|
|
52
|
+
'alter table public."profiles" enable row level security',
|
|
53
|
+
);
|
|
54
|
+
expect(reconciliation?.content).toContain(
|
|
41
55
|
'revoke all on table public."profiles" from anon, authenticated',
|
|
42
56
|
);
|
|
43
|
-
expect(
|
|
57
|
+
expect(reconciliation?.content).toContain(
|
|
44
58
|
'grant select on table public."profiles" to authenticated',
|
|
45
59
|
);
|
|
46
|
-
expect(
|
|
60
|
+
expect(reconciliation?.content).toContain(
|
|
47
61
|
'grant update ("email", "display_name", "avatar_url") on table public."profiles" to authenticated',
|
|
48
62
|
);
|
|
49
|
-
expect(
|
|
50
|
-
expect(
|
|
51
|
-
expect(
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
54
|
-
expect(
|
|
55
|
-
expect(
|
|
63
|
+
expect(reconciliation?.content).not.toContain('role text not null');
|
|
64
|
+
expect(reconciliation?.content).not.toContain('grant update ("role")');
|
|
65
|
+
expect(reconciliation?.content).toContain('create policy "profiles_select_own"');
|
|
66
|
+
expect(reconciliation?.content).toContain('to authenticated');
|
|
67
|
+
expect(reconciliation?.content).toContain('create policy "profiles_update_own"');
|
|
68
|
+
expect(reconciliation?.content).toContain('using ((select auth.uid()) = id)');
|
|
69
|
+
expect(reconciliation?.content).toContain('create schema if not exists ankhorage_internal');
|
|
70
|
+
expect(reconciliation?.content).toContain('ankhorage_internal.generated_schema_state');
|
|
71
|
+
expect(reconciliation?.content).toContain('table_name text');
|
|
72
|
+
expect(reconciliation?.content).toContain('profile.table changed from % to %');
|
|
73
|
+
expect(reconciliation?.content).toContain(
|
|
74
|
+
'existing auth.profile generated state is missing table identity',
|
|
75
|
+
);
|
|
76
|
+
expect(reconciliation?.content).toContain("'auth.profile'");
|
|
77
|
+
expect(reconciliation?.content).toContain('create trigger "on_auth_user_created_profiles"');
|
|
78
|
+
expect(reconciliation?.content).toContain('after insert on auth.users');
|
|
79
|
+
expect(reconciliation?.content).toContain(
|
|
80
|
+
'execute function public."handle_new_profiles_user"()',
|
|
81
|
+
);
|
|
82
|
+
expect(reconciliation?.content).toContain(
|
|
83
|
+
'revoke execute on function public."handle_new_profiles_user"() from PUBLIC',
|
|
84
|
+
);
|
|
85
|
+
expect(reconciliation?.content).toContain('set search_path = pg_catalog, pg_temp');
|
|
86
|
+
expect(reconciliation?.content).not.toContain('set search_path = public, auth, pg_temp');
|
|
87
|
+
expect(reconciliation?.content).not.toContain('create table if not exists public.users');
|
|
56
88
|
});
|
|
57
89
|
|
|
58
|
-
test('does not generate
|
|
90
|
+
test('does not generate profile reconciliation when profile table is omitted', () => {
|
|
59
91
|
const artifacts = generateSupabaseAuthArtifacts({
|
|
60
92
|
namespace: 'scan-app',
|
|
61
93
|
manifest: createManifest({
|
|
@@ -65,8 +97,110 @@ describe('generateSupabaseAuthArtifacts profile tables', () => {
|
|
|
65
97
|
}),
|
|
66
98
|
});
|
|
67
99
|
|
|
100
|
+
expect(artifacts.files.some((file) => file.path.includes('supabase/generated'))).toBe(false);
|
|
68
101
|
expect(artifacts.files.some((file) => file.path.includes('supabase/migrations'))).toBe(false);
|
|
69
102
|
});
|
|
103
|
+
|
|
104
|
+
test('preserves an explicitly empty profile field list', () => {
|
|
105
|
+
const artifacts = generateSupabaseAuthArtifacts({
|
|
106
|
+
namespace: 'scan-app',
|
|
107
|
+
manifest: createManifest({
|
|
108
|
+
profile: {
|
|
109
|
+
fields: [],
|
|
110
|
+
table: 'profiles',
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const reconciliation = artifacts.files.find(
|
|
116
|
+
(file) => file.path === 'infra/minikube/supabase/generated/auth_profiles.sql',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(reconciliation?.content).not.toContain('add column if not exists "email"');
|
|
120
|
+
expect(reconciliation?.content).not.toContain('grant update (');
|
|
121
|
+
expect(reconciliation?.content).toContain(
|
|
122
|
+
'alter table public."profiles" drop column if exists "email"',
|
|
123
|
+
);
|
|
124
|
+
expect(reconciliation?.content).toContain(
|
|
125
|
+
'alter table public."profiles" drop column if exists "first_name"',
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('rejects unsupported configured profile fields', () => {
|
|
130
|
+
expect(() =>
|
|
131
|
+
generateSupabaseAuthArtifacts({
|
|
132
|
+
namespace: 'scan-app',
|
|
133
|
+
manifest: createManifest({
|
|
134
|
+
profile: {
|
|
135
|
+
fields: ['email', 'bio'],
|
|
136
|
+
table: 'profiles',
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
}),
|
|
140
|
+
).toThrow('Unsupported Supabase profile field "bio"');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('rejects blank configured profile fields', () => {
|
|
144
|
+
expect(() =>
|
|
145
|
+
generateSupabaseAuthArtifacts({
|
|
146
|
+
namespace: 'scan-app',
|
|
147
|
+
manifest: createManifest({
|
|
148
|
+
profile: {
|
|
149
|
+
fields: ['email', ' '],
|
|
150
|
+
table: 'profiles',
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
}),
|
|
154
|
+
).toThrow('Profile fields must be non-empty');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('uses a stable desired-state hash and canonical managed column ordering', () => {
|
|
158
|
+
const first = generateSupabaseAuthArtifacts({
|
|
159
|
+
namespace: 'scan-app',
|
|
160
|
+
manifest: createManifest({
|
|
161
|
+
profile: {
|
|
162
|
+
fields: ['avatarUrl', 'email', 'displayName'],
|
|
163
|
+
table: 'profiles',
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
});
|
|
167
|
+
const second = generateSupabaseAuthArtifacts({
|
|
168
|
+
namespace: 'scan-app',
|
|
169
|
+
manifest: createManifest({
|
|
170
|
+
profile: {
|
|
171
|
+
fields: ['displayName', 'avatarUrl', 'email'],
|
|
172
|
+
table: 'profiles',
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const firstSql = first.files.find(
|
|
178
|
+
(file) => file.path === 'infra/minikube/supabase/generated/auth_profiles.sql',
|
|
179
|
+
)?.content;
|
|
180
|
+
const secondSql = second.files.find(
|
|
181
|
+
(file) => file.path === 'infra/minikube/supabase/generated/auth_profiles.sql',
|
|
182
|
+
)?.content;
|
|
183
|
+
|
|
184
|
+
expect(firstSql).toBe(secondSql);
|
|
185
|
+
expect(firstSql?.indexOf('"email"')).toBeLessThan(firstSql?.indexOf('"display_name"') ?? -1);
|
|
186
|
+
expect(firstSql?.indexOf('"display_name"')).toBeLessThan(
|
|
187
|
+
firstSql?.indexOf('"avatar_url"') ?? -1,
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('rejects public.users as a generated profile table before SQL is emitted', () => {
|
|
192
|
+
expect(() =>
|
|
193
|
+
generateSupabaseAuthArtifacts({
|
|
194
|
+
namespace: 'scan-app',
|
|
195
|
+
manifest: createManifest({
|
|
196
|
+
profile: {
|
|
197
|
+
fields: ['email'],
|
|
198
|
+
table: 'users',
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
}),
|
|
202
|
+
).toThrow('Invalid Supabase profile table identifier "users"');
|
|
203
|
+
});
|
|
70
204
|
});
|
|
71
205
|
|
|
72
206
|
function createManifest(auth: Pick<NonNullable<InfraManifest['auth']>, 'profile'>): InfraManifest {
|