@ankhorage/infra 0.3.2 → 0.3.3

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @ankhorage/infra
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 77be403: Make generated local Supabase profile schema deterministic by separating immutable migrations from generated reconciliation, applying profile desired state during local startup, and verifying the live database schema.
8
+
3
9
  ## 0.3.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # INFRA
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.3.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.3.2](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Executable infra provider and standalone CLI for Ankhorage project workflows.
9
9
 
@@ -36,7 +36,7 @@ Project resolution is project-aware:
36
36
  Source: `src/readme-usage.ts`
37
37
 
38
38
  ```ts
39
- import { runCli } from './cli.js';
39
+ import { runCli } from './cli/bin.js';
40
40
 
41
41
  await runCli(['--help']);
42
42
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/minikube/auth/supabase/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEhE,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,wBAAwB,CA6E3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/minikube/auth/supabase/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAOhE,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,wBAAwB,CA6E3B"}
@@ -1,14 +1,15 @@
1
+ import { getSupabaseProfileReconciliation, resolveSupabaseProfileModel, } from './profile';
1
2
  export function generateSupabaseAuthArtifacts(args) {
2
3
  const { manifest, namespace } = args;
3
4
  const root = 'infra/minikube/k8s/auth/supabase';
4
5
  const resourceRoot = 'auth/supabase';
5
6
  const docsRoot = 'infra/minikube/auth';
6
- const migrationRoot = 'infra/minikube/supabase/migrations';
7
+ const generatedRoot = 'infra/minikube/supabase/generated';
7
8
  const scope = manifest.auth?.scope ?? 'global';
8
9
  const authzEngine = manifest.auth?.authorization.engine ?? 'native';
9
10
  const authzKind = manifest.auth?.authorization.kind ?? 'RBAC';
10
11
  const authFieldModel = resolveAuthFieldModel(manifest);
11
- const profileModel = resolveProfileModel(manifest);
12
+ const profileModel = resolveSupabaseProfileModel(manifest);
12
13
  const warnings = [];
13
14
  if (scope !== 'global') {
14
15
  warnings.push(`Auth scope "${scope}" is not fully modeled yet for Supabase. Baseline resources assume global auth.`);
@@ -44,8 +45,8 @@ export function generateSupabaseAuthArtifacts(args) {
44
45
  ...(profileModel.enabled
45
46
  ? [
46
47
  {
47
- path: `${migrationRoot}/0001_auth_profiles.sql`,
48
- content: getSupabaseProfileMigration(profileModel),
48
+ path: `${generatedRoot}/auth_profiles.sql`,
49
+ content: getSupabaseProfileReconciliation(profileModel),
49
50
  },
50
51
  ]
51
52
  : []),
@@ -144,12 +145,12 @@ function getSupabaseRuntimeWiringGuide(profileModel) {
144
145
  Identity remains provider-owned in \`auth.users\`. App-facing profile data is generated in
145
146
  \`public.${profileModel.table}\` and linked through \`${profileModel.table}.id -> auth.users.id\`.
146
147
 
147
- The generated migration:
148
+ The generated profile reconciliation:
148
149
 
149
150
  - creates the profile table
150
151
  - enables row-level security
151
152
  - allows signed-in users to read and update their own profile row
152
- - protects the generated \`role\` column from self-service profile updates
153
+ - drops stale Ankhorage-managed profile columns that are no longer configured
153
154
  - creates a trigger for new auth users when \`AUTH_PROFILE_CREATE_STRATEGY=trigger\`
154
155
  `
155
156
  : '';
@@ -217,16 +218,16 @@ const DEFAULT_SIGN_IN_IDENTIFIERS = ['email'];
217
218
  const DEFAULT_SIGN_UP_REQUIRED_FIELDS = ['email', 'password'];
218
219
  const DEFAULT_SIGN_UP_OPTIONAL_FIELDS = ['firstName', 'lastName'];
219
220
  const DEFAULT_SIGN_UP_POLICY = 'autoSignIn';
220
- const DEFAULT_PROFILE_FIELDS = ['email', 'firstName', 'lastName'];
221
- const DEFAULT_PROFILE_PRIMARY_KEY = 'authUserId';
222
- const DEFAULT_PROFILE_CREATE_STRATEGY = 'trigger';
223
- const DEFAULT_PROFILE_UPDATE_STRATEGY = 'api';
224
221
  function resolveAuthFieldModel(manifest) {
225
222
  const signInIdentifiers = normalizeFieldList(manifest.auth?.signIn?.identifiers, DEFAULT_SIGN_IN_IDENTIFIERS);
226
223
  const signUpRequiredFields = normalizeFieldList(manifest.auth?.signUp?.requiredFields, DEFAULT_SIGN_UP_REQUIRED_FIELDS);
227
224
  const signUpOptionalFields = normalizeFieldList(manifest.auth?.signUp?.optionalFields, DEFAULT_SIGN_UP_OPTIONAL_FIELDS).filter((field) => !signUpRequiredFields.includes(field));
228
225
  const signUpPolicy = manifest.auth?.signUp?.signUpPolicy ?? DEFAULT_SIGN_UP_POLICY;
229
- const profileFields = normalizeFieldList(manifest.auth?.profile?.fields, DEFAULT_PROFILE_FIELDS);
226
+ const profileFields = normalizeFieldList(manifest.auth?.profile?.fields, [
227
+ 'email',
228
+ 'firstName',
229
+ 'lastName',
230
+ ]);
230
231
  return {
231
232
  signInIdentifiersCsv: signInIdentifiers.join(','),
232
233
  signUpRequiredFieldsCsv: signUpRequiredFields.join(','),
@@ -235,144 +236,6 @@ function resolveAuthFieldModel(manifest) {
235
236
  profileFieldsCsv: profileFields.join(','),
236
237
  };
237
238
  }
238
- function resolveProfileModel(manifest) {
239
- const table = normalizeIdentifier(manifest.auth?.profile?.table ?? '');
240
- const fields = normalizeFieldList(manifest.auth?.profile?.fields, DEFAULT_PROFILE_FIELDS);
241
- return {
242
- enabled: table.length > 0,
243
- table,
244
- primaryKey: manifest.auth?.profile?.primaryKey ?? DEFAULT_PROFILE_PRIMARY_KEY,
245
- createStrategy: manifest.auth?.profile?.createStrategy ?? DEFAULT_PROFILE_CREATE_STRATEGY,
246
- updateStrategy: manifest.auth?.profile?.updateStrategy ?? DEFAULT_PROFILE_UPDATE_STRATEGY,
247
- columns: resolveProfileColumns(fields),
248
- };
249
- }
250
- function getSupabaseProfileMigration(profileModel) {
251
- const table = quoteIdentifier(profileModel.table);
252
- const functionName = quoteIdentifier(`handle_new_${profileModel.table}_user`);
253
- const triggerName = quoteIdentifier(`on_auth_user_created_${profileModel.table}`);
254
- const columnDefinitions = profileModel.columns
255
- .map((column) => ` ${quoteIdentifier(column.column)} ${column.sqlType}`)
256
- .join(',\n');
257
- const insertColumns = ['id', ...profileModel.columns.map((column) => column.column)]
258
- .map(quoteIdentifier)
259
- .join(', ');
260
- const insertValues = ['new.id', ...profileModel.columns.map((column) => column.fromNewUser)].join(', ');
261
- const updateAssignments = profileModel.columns
262
- .filter((column) => column.field === 'email')
263
- .map((column) => `${quoteIdentifier(column.column)} = excluded.${quoteIdentifier(column.column)}`);
264
- const conflictUpdate = [...updateAssignments, 'updated_at = now()'].join(',\n ');
265
- const updateGrantColumns = profileModel.columns.map((column) => quoteIdentifier(column.column));
266
- const updateGrantSql = updateGrantColumns.length > 0
267
- ? `grant update (${updateGrantColumns.join(', ')}) on table public.${table} to authenticated;\n`
268
- : '';
269
- const triggerSql = profileModel.createStrategy === 'trigger'
270
- ? `
271
- create or replace function public.${functionName}()
272
- returns trigger
273
- language plpgsql
274
- security definer
275
- set search_path = public
276
- as $$
277
- begin
278
- insert into public.${table} (${insertColumns})
279
- values (${insertValues})
280
- on conflict (id) do update set
281
- ${conflictUpdate};
282
-
283
- return new;
284
- end;
285
- $$;
286
-
287
- drop trigger if exists ${triggerName} on auth.users;
288
- create trigger ${triggerName}
289
- after insert on auth.users
290
- for each row execute function public.${functionName}();
291
- `
292
- : '';
293
- return `-- Generated by @ankhorage/infra from manifest.infra.auth.profile.
294
- -- Supabase Auth owns identity in auth.users. App-facing profile data lives in public.${profileModel.table}.
295
-
296
- create table if not exists public.${table} (
297
- id uuid primary key references auth.users(id) on delete cascade${columnDefinitions ? `,\n${columnDefinitions}` : ''},
298
- role text not null default 'user',
299
- created_at timestamptz not null default now(),
300
- updated_at timestamptz not null default now()
301
- );
302
-
303
- alter table public.${table} enable row level security;
304
-
305
- revoke all on table public.${table} from anon, authenticated;
306
- grant select on table public.${table} to authenticated;
307
- ${updateGrantSql}
308
- create policy ${quoteIdentifier(`${profileModel.table}_select_own`)}
309
- on public.${table}
310
- for select
311
- using (auth.uid() = id);
312
-
313
- create policy ${quoteIdentifier(`${profileModel.table}_update_own`)}
314
- on public.${table}
315
- for update
316
- using (auth.uid() = id)
317
- with check (auth.uid() = id);
318
- ${triggerSql}`;
319
- }
320
- function resolveProfileColumns(fields) {
321
- const columns = new Map();
322
- for (const field of fields) {
323
- const column = mapProfileFieldToColumn(field);
324
- if (!column || columns.has(column.column)) {
325
- continue;
326
- }
327
- columns.set(column.column, column);
328
- }
329
- return [...columns.values()];
330
- }
331
- function mapProfileFieldToColumn(field) {
332
- switch (field) {
333
- case 'email':
334
- return { field, column: 'email', sqlType: 'text', fromNewUser: 'new.email' };
335
- case 'displayName':
336
- return {
337
- field,
338
- column: 'display_name',
339
- sqlType: 'text',
340
- fromNewUser: "coalesce(new.raw_user_meta_data->>'displayName', new.raw_user_meta_data->>'display_name', split_part(new.email, '@', 1))",
341
- };
342
- case 'firstName':
343
- return {
344
- field,
345
- column: 'first_name',
346
- sqlType: 'text',
347
- fromNewUser: "coalesce(new.raw_user_meta_data->>'firstName', new.raw_user_meta_data->>'first_name')",
348
- };
349
- case 'lastName':
350
- return {
351
- field,
352
- column: 'last_name',
353
- sqlType: 'text',
354
- fromNewUser: "coalesce(new.raw_user_meta_data->>'lastName', new.raw_user_meta_data->>'last_name')",
355
- };
356
- case 'avatarUrl':
357
- return {
358
- field,
359
- column: 'avatar_url',
360
- sqlType: 'text',
361
- fromNewUser: "coalesce(new.raw_user_meta_data->>'avatarUrl', new.raw_user_meta_data->>'avatar_url')",
362
- };
363
- case 'username':
364
- return {
365
- field,
366
- column: 'username',
367
- sqlType: 'text',
368
- fromNewUser: "new.raw_user_meta_data->>'username'",
369
- };
370
- case 'phone':
371
- return { field, column: 'phone', sqlType: 'text', fromNewUser: 'new.phone' };
372
- default:
373
- return null;
374
- }
375
- }
376
239
  function normalizeFieldList(values, fallback) {
377
240
  const source = values ?? fallback;
378
241
  const next = [];
@@ -388,18 +251,6 @@ function normalizeFieldList(values, fallback) {
388
251
  }
389
252
  return [...fallback];
390
253
  }
391
- function normalizeIdentifier(value) {
392
- const trimmed = value.trim();
393
- if (!trimmed)
394
- return '';
395
- if (!/^[a-z_][a-z0-9_]*$/.test(trimmed)) {
396
- throw new Error(`Invalid Supabase profile table identifier "${trimmed}". Use snake_case letters, numbers, and underscores.`);
397
- }
398
- return trimmed;
399
- }
400
- function quoteIdentifier(value) {
401
- return `"${value.replaceAll('"', '""')}"`;
402
- }
403
254
  function escapeYamlDoubleQuoted(value) {
404
255
  return value.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
405
256
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/adapters/minikube/auth/supabase/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InfraManifestInput } from '../../../../types';\nimport type { MinikubeAdapterArtifacts } from '../../contracts';\n\nexport function generateSupabaseAuthArtifacts(args: {\n manifest: InfraManifestInput;\n namespace: string;\n}): MinikubeAdapterArtifacts {\n const { manifest, namespace } = args;\n\n const root = 'infra/minikube/k8s/auth/supabase';\n const resourceRoot = 'auth/supabase';\n const docsRoot = 'infra/minikube/auth';\n const migrationRoot = 'infra/minikube/supabase/migrations';\n const scope = manifest.auth?.scope ?? 'global';\n const authzEngine = manifest.auth?.authorization.engine ?? 'native';\n const authzKind = manifest.auth?.authorization.kind ?? 'RBAC';\n const authFieldModel = resolveAuthFieldModel(manifest);\n const profileModel = resolveProfileModel(manifest);\n\n const warnings: string[] = [];\n if (scope !== 'global') {\n warnings.push(\n `Auth scope \"${scope}\" is not fully modeled yet for Supabase. Baseline resources assume global auth.`,\n );\n }\n\n return {\n files: [\n {\n path: `${root}/supabase-auth.configmap.yaml`,\n content: getSupabaseConfigMap({\n namespace,\n scope,\n authzEngine,\n authzKind,\n authFieldModel,\n profileModel,\n }),\n },\n {\n path: `${root}/supabase-auth.secret.yaml`,\n content: getSupabaseSecret(namespace),\n },\n {\n path: `${root}/app-runtime-auth.env.configmap.yaml`,\n content: getRuntimeAuthEnvConfigMap({\n namespace,\n scope,\n authzEngine,\n authzKind,\n authFieldModel,\n profileModel,\n }),\n },\n ...(profileModel.enabled\n ? [\n {\n path: `${migrationRoot}/0001_auth_profiles.sql`,\n content: getSupabaseProfileMigration(profileModel),\n },\n ]\n : []),\n {\n path: `${docsRoot}/supabase-runtime-wiring.md`,\n content: getSupabaseRuntimeWiringGuide(profileModel),\n },\n ],\n resources: [\n `${resourceRoot}/supabase-auth.configmap.yaml`,\n `${resourceRoot}/supabase-auth.secret.yaml`,\n `${resourceRoot}/app-runtime-auth.env.configmap.yaml`,\n ],\n envEntries: [\n 'SUPABASE_SECRET_SYNC_ENABLED=true',\n 'SUPABASE_URL=',\n 'SUPABASE_ANON_KEY=',\n 'SUPABASE_SERVICE_ROLE_KEY=',\n 'SUPABASE_JWT_SECRET=',\n 'EXPO_PUBLIC_SUPABASE_URL=',\n 'EXPO_PUBLIC_SUPABASE_ANON_KEY=',\n ],\n warnings,\n };\n}\n\nfunction getSupabaseConfigMap(args: {\n namespace: string;\n scope: string;\n authzEngine: string;\n authzKind: string;\n authFieldModel: ResolvedAuthFieldModel;\n profileModel: ResolvedProfileModel;\n}) {\n const { namespace, scope, authzEngine, authzKind, authFieldModel, profileModel } = args;\n\n return `apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: supabase-auth-config\n namespace: ${namespace}\ndata:\n AUTH_PROVIDER: \"supabase\"\n AUTH_SCOPE: \"${scope}\"\n AUTHZ_ENGINE: \"${authzEngine}\"\n AUTHZ_KIND: \"${authzKind}\"\n AUTH_SIGN_IN_IDENTIFIERS: \"${escapeYamlDoubleQuoted(authFieldModel.signInIdentifiersCsv)}\"\n AUTH_SIGN_UP_REQUIRED_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpRequiredFieldsCsv)}\"\n AUTH_SIGN_UP_OPTIONAL_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpOptionalFieldsCsv)}\"\n AUTH_SIGN_UP_POLICY: \"${authFieldModel.signUpPolicy}\"\n AUTH_PROFILE_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.profileFieldsCsv)}\"\n AUTH_PROFILE_TABLE: \"${escapeYamlDoubleQuoted(profileModel.table)}\"\n AUTH_PROFILE_PRIMARY_KEY: \"${profileModel.primaryKey}\"\n AUTH_PROFILE_CREATE_STRATEGY: \"${profileModel.createStrategy}\"\n AUTH_PROFILE_UPDATE_STRATEGY: \"${profileModel.updateStrategy}\"\n`;\n}\n\nfunction getSupabaseSecret(namespace: string) {\n return `apiVersion: v1\nkind: Secret\nmetadata:\n name: supabase-auth-secrets\n namespace: ${namespace}\ntype: Opaque\nstringData:\n SUPABASE_URL: \"\"\n SUPABASE_ANON_KEY: \"\"\n SUPABASE_SERVICE_ROLE_KEY: \"\"\n SUPABASE_JWT_SECRET: \"\"\n EXPO_PUBLIC_SUPABASE_URL: \"\"\n EXPO_PUBLIC_SUPABASE_ANON_KEY: \"\"\n`;\n}\n\nfunction getRuntimeAuthEnvConfigMap(args: {\n namespace: string;\n scope: string;\n authzEngine: string;\n authzKind: string;\n authFieldModel: ResolvedAuthFieldModel;\n profileModel: ResolvedProfileModel;\n}) {\n const { namespace, scope, authzEngine, authzKind, authFieldModel, profileModel } = args;\n\n return `apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: app-runtime-auth-env\n namespace: ${namespace}\ndata:\n AUTH_REQUIRED: \"true\"\n AUTH_PROVIDER: \"supabase\"\n AUTH_SCOPE: \"${scope}\"\n AUTHZ_ENGINE: \"${authzEngine}\"\n AUTHZ_KIND: \"${authzKind}\"\n AUTH_SIGN_IN_IDENTIFIERS: \"${escapeYamlDoubleQuoted(authFieldModel.signInIdentifiersCsv)}\"\n AUTH_SIGN_UP_REQUIRED_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpRequiredFieldsCsv)}\"\n AUTH_SIGN_UP_OPTIONAL_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpOptionalFieldsCsv)}\"\n AUTH_SIGN_UP_POLICY: \"${authFieldModel.signUpPolicy}\"\n AUTH_PROFILE_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.profileFieldsCsv)}\"\n AUTH_PROFILE_TABLE: \"${escapeYamlDoubleQuoted(profileModel.table)}\"\n AUTH_PROFILE_PRIMARY_KEY: \"${profileModel.primaryKey}\"\n AUTH_PROFILE_CREATE_STRATEGY: \"${profileModel.createStrategy}\"\n AUTH_PROFILE_UPDATE_STRATEGY: \"${profileModel.updateStrategy}\"\n AUTH_TOKEN_HEADER: \"Authorization\"\n AUTH_TOKEN_PREFIX: \"Bearer\"\n`;\n}\n\nfunction getSupabaseRuntimeWiringGuide(profileModel: ResolvedProfileModel) {\n const profileSection = profileModel.enabled\n ? `\n## App Profile Table\n\nIdentity remains provider-owned in \\`auth.users\\`. App-facing profile data is generated in\n\\`public.${profileModel.table}\\` and linked through \\`${profileModel.table}.id -> auth.users.id\\`.\n\nThe generated migration:\n\n- creates the profile table\n- enables row-level security\n- allows signed-in users to read and update their own profile row\n- protects the generated \\`role\\` column from self-service profile updates\n- creates a trigger for new auth users when \\`AUTH_PROFILE_CREATE_STRATEGY=trigger\\`\n`\n : '';\n\n return `# Supabase Runtime Wiring\n\nThis file documents the runtime conventions generated by the minikube Supabase adapter.\n\n## Generated Kubernetes Objects\n\n- \\`ConfigMap/supabase-auth-config\\`: high-level auth metadata.\n- \\`Secret/supabase-auth-secrets\\`: Supabase keys and public runtime keys.\n- \\`ConfigMap/app-runtime-auth-env\\`: auth runtime feature flags and authz metadata.\n\n## App Runtime Convention\n\nYour app runtime deployment should import both:\n\n- \\`app-runtime-auth-env\\` (configMap)\n- \\`supabase-auth-secrets\\` (secret)\n\nExample deployment fragment:\n\n\\`\\`\\`yaml\nenvFrom:\n - configMapRef:\n name: app-runtime-auth-env\n - secretRef:\n name: supabase-auth-secrets\n\\`\\`\\`\n\nThe generated base deployment at \\`k8s/app/deployment.yaml\\` already includes this wiring\nwith \\`optional: true\\` for auth sources.\n\n## Expected Environment Variables\n\n- \\`AUTH_REQUIRED\\`\n- \\`AUTH_PROVIDER\\`\n- \\`AUTH_SCOPE\\`\n- \\`AUTHZ_ENGINE\\`\n- \\`AUTHZ_KIND\\`\n- \\`AUTH_SIGN_IN_IDENTIFIERS\\`\n- \\`AUTH_SIGN_UP_REQUIRED_FIELDS\\`\n- \\`AUTH_SIGN_UP_OPTIONAL_FIELDS\\`\n- \\`AUTH_SIGN_UP_POLICY\\`\n- \\`AUTH_PROFILE_FIELDS\\`\n- \\`AUTH_PROFILE_TABLE\\`\n- \\`AUTH_PROFILE_PRIMARY_KEY\\`\n- \\`AUTH_PROFILE_CREATE_STRATEGY\\`\n- \\`AUTH_PROFILE_UPDATE_STRATEGY\\`\n- \\`SUPABASE_SECRET_SYNC_ENABLED\\`\n- \\`SUPABASE_URL\\`\n- \\`SUPABASE_ANON_KEY\\`\n- \\`SUPABASE_SERVICE_ROLE_KEY\\`\n- \\`SUPABASE_JWT_SECRET\\`\n- \\`EXPO_PUBLIC_SUPABASE_URL\\`\n- \\`EXPO_PUBLIC_SUPABASE_ANON_KEY\\`\n${profileSection}\n## Secret Sync\n\nWhen \\`SUPABASE_SECRET_SYNC_ENABLED=true\\`, \\`scripts/up.sh\\` will create/update\n\\`Secret/supabase-auth-secrets\\` from the loaded environment values.\n`;\n}\n\nconst DEFAULT_SIGN_IN_IDENTIFIERS = ['email'];\nconst DEFAULT_SIGN_UP_REQUIRED_FIELDS = ['email', 'password'];\nconst DEFAULT_SIGN_UP_OPTIONAL_FIELDS = ['firstName', 'lastName'];\nconst DEFAULT_SIGN_UP_POLICY = 'autoSignIn';\nconst DEFAULT_PROFILE_FIELDS = ['email', 'firstName', 'lastName'];\nconst DEFAULT_PROFILE_PRIMARY_KEY = 'authUserId';\nconst DEFAULT_PROFILE_CREATE_STRATEGY = 'trigger';\nconst DEFAULT_PROFILE_UPDATE_STRATEGY = 'api';\n\ninterface ResolvedAuthFieldModel {\n signInIdentifiersCsv: string;\n signUpRequiredFieldsCsv: string;\n signUpOptionalFieldsCsv: string;\n signUpPolicy: string;\n profileFieldsCsv: string;\n}\n\ninterface ProfileColumnSpec {\n readonly field: string;\n readonly column: string;\n readonly sqlType: string;\n readonly fromNewUser: string;\n}\n\ninterface ResolvedProfileModel {\n readonly enabled: boolean;\n readonly table: string;\n readonly primaryKey: string;\n readonly createStrategy: string;\n readonly updateStrategy: string;\n readonly columns: readonly ProfileColumnSpec[];\n}\n\nfunction resolveAuthFieldModel(manifest: InfraManifestInput): ResolvedAuthFieldModel {\n const signInIdentifiers = normalizeFieldList(\n manifest.auth?.signIn?.identifiers,\n DEFAULT_SIGN_IN_IDENTIFIERS,\n );\n const signUpRequiredFields = normalizeFieldList(\n manifest.auth?.signUp?.requiredFields,\n DEFAULT_SIGN_UP_REQUIRED_FIELDS,\n );\n const signUpOptionalFields = normalizeFieldList(\n manifest.auth?.signUp?.optionalFields,\n DEFAULT_SIGN_UP_OPTIONAL_FIELDS,\n ).filter((field) => !signUpRequiredFields.includes(field));\n const signUpPolicy = manifest.auth?.signUp?.signUpPolicy ?? DEFAULT_SIGN_UP_POLICY;\n const profileFields = normalizeFieldList(manifest.auth?.profile?.fields, DEFAULT_PROFILE_FIELDS);\n\n return {\n signInIdentifiersCsv: signInIdentifiers.join(','),\n signUpRequiredFieldsCsv: signUpRequiredFields.join(','),\n signUpOptionalFieldsCsv: signUpOptionalFields.join(','),\n signUpPolicy,\n profileFieldsCsv: profileFields.join(','),\n };\n}\n\nfunction resolveProfileModel(manifest: InfraManifestInput): ResolvedProfileModel {\n const table = normalizeIdentifier(manifest.auth?.profile?.table ?? '');\n const fields = normalizeFieldList(manifest.auth?.profile?.fields, DEFAULT_PROFILE_FIELDS);\n\n return {\n enabled: table.length > 0,\n table,\n primaryKey: manifest.auth?.profile?.primaryKey ?? DEFAULT_PROFILE_PRIMARY_KEY,\n createStrategy: manifest.auth?.profile?.createStrategy ?? DEFAULT_PROFILE_CREATE_STRATEGY,\n updateStrategy: manifest.auth?.profile?.updateStrategy ?? DEFAULT_PROFILE_UPDATE_STRATEGY,\n columns: resolveProfileColumns(fields),\n };\n}\n\nfunction getSupabaseProfileMigration(profileModel: ResolvedProfileModel): string {\n const table = quoteIdentifier(profileModel.table);\n const functionName = quoteIdentifier(`handle_new_${profileModel.table}_user`);\n const triggerName = quoteIdentifier(`on_auth_user_created_${profileModel.table}`);\n const columnDefinitions = profileModel.columns\n .map((column) => ` ${quoteIdentifier(column.column)} ${column.sqlType}`)\n .join(',\\n');\n const insertColumns = ['id', ...profileModel.columns.map((column) => column.column)]\n .map(quoteIdentifier)\n .join(', ');\n const insertValues = ['new.id', ...profileModel.columns.map((column) => column.fromNewUser)].join(\n ', ',\n );\n const updateAssignments = profileModel.columns\n .filter((column) => column.field === 'email')\n .map(\n (column) => `${quoteIdentifier(column.column)} = excluded.${quoteIdentifier(column.column)}`,\n );\n const conflictUpdate = [...updateAssignments, 'updated_at = now()'].join(',\\n ');\n const updateGrantColumns = profileModel.columns.map((column) => quoteIdentifier(column.column));\n const updateGrantSql =\n updateGrantColumns.length > 0\n ? `grant update (${updateGrantColumns.join(', ')}) on table public.${table} to authenticated;\\n`\n : '';\n const triggerSql =\n profileModel.createStrategy === 'trigger'\n ? `\ncreate or replace function public.${functionName}()\nreturns trigger\nlanguage plpgsql\nsecurity definer\nset search_path = public\nas $$\nbegin\n insert into public.${table} (${insertColumns})\n values (${insertValues})\n on conflict (id) do update set\n ${conflictUpdate};\n\n return new;\nend;\n$$;\n\ndrop trigger if exists ${triggerName} on auth.users;\ncreate trigger ${triggerName}\n after insert on auth.users\n for each row execute function public.${functionName}();\n`\n : '';\n\n return `-- Generated by @ankhorage/infra from manifest.infra.auth.profile.\n-- Supabase Auth owns identity in auth.users. App-facing profile data lives in public.${profileModel.table}.\n\ncreate table if not exists public.${table} (\n id uuid primary key references auth.users(id) on delete cascade${\n columnDefinitions ? `,\\n${columnDefinitions}` : ''\n },\n role text not null default 'user',\n created_at timestamptz not null default now(),\n updated_at timestamptz not null default now()\n);\n\nalter table public.${table} enable row level security;\n\nrevoke all on table public.${table} from anon, authenticated;\ngrant select on table public.${table} to authenticated;\n${updateGrantSql}\ncreate policy ${quoteIdentifier(`${profileModel.table}_select_own`)}\n on public.${table}\n for select\n using (auth.uid() = id);\n\ncreate policy ${quoteIdentifier(`${profileModel.table}_update_own`)}\n on public.${table}\n for update\n using (auth.uid() = id)\n with check (auth.uid() = id);\n${triggerSql}`;\n}\n\nfunction resolveProfileColumns(fields: readonly string[]): ProfileColumnSpec[] {\n const columns = new Map<string, ProfileColumnSpec>();\n\n for (const field of fields) {\n const column = mapProfileFieldToColumn(field);\n if (!column || columns.has(column.column)) {\n continue;\n }\n columns.set(column.column, column);\n }\n\n return [...columns.values()];\n}\n\nfunction mapProfileFieldToColumn(field: string): ProfileColumnSpec | null {\n switch (field) {\n case 'email':\n return { field, column: 'email', sqlType: 'text', fromNewUser: 'new.email' };\n case 'displayName':\n return {\n field,\n column: 'display_name',\n sqlType: 'text',\n fromNewUser:\n \"coalesce(new.raw_user_meta_data->>'displayName', new.raw_user_meta_data->>'display_name', split_part(new.email, '@', 1))\",\n };\n case 'firstName':\n return {\n field,\n column: 'first_name',\n sqlType: 'text',\n fromNewUser:\n \"coalesce(new.raw_user_meta_data->>'firstName', new.raw_user_meta_data->>'first_name')\",\n };\n case 'lastName':\n return {\n field,\n column: 'last_name',\n sqlType: 'text',\n fromNewUser:\n \"coalesce(new.raw_user_meta_data->>'lastName', new.raw_user_meta_data->>'last_name')\",\n };\n case 'avatarUrl':\n return {\n field,\n column: 'avatar_url',\n sqlType: 'text',\n fromNewUser:\n \"coalesce(new.raw_user_meta_data->>'avatarUrl', new.raw_user_meta_data->>'avatar_url')\",\n };\n case 'username':\n return {\n field,\n column: 'username',\n sqlType: 'text',\n fromNewUser: \"new.raw_user_meta_data->>'username'\",\n };\n case 'phone':\n return { field, column: 'phone', sqlType: 'text', fromNewUser: 'new.phone' };\n default:\n return null;\n }\n}\n\nfunction normalizeFieldList(\n values: readonly string[] | undefined,\n fallback: readonly string[],\n): string[] {\n const source = values ?? fallback;\n const next: string[] = [];\n\n for (const value of source) {\n const normalized = value.trim();\n if (!normalized || next.includes(normalized)) {\n continue;\n }\n next.push(normalized);\n }\n\n if (next.length > 0) {\n return next;\n }\n\n return [...fallback];\n}\n\nfunction normalizeIdentifier(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return '';\n\n if (!/^[a-z_][a-z0-9_]*$/.test(trimmed)) {\n throw new Error(\n `Invalid Supabase profile table identifier \"${trimmed}\". Use snake_case letters, numbers, and underscores.`,\n );\n }\n\n return trimmed;\n}\n\nfunction quoteIdentifier(value: string): string {\n return `\"${value.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction escapeYamlDoubleQuoted(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"');\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/adapters/minikube/auth/supabase/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAEhC,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AAEnB,MAAM,UAAU,6BAA6B,CAAC,IAG7C;IACC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAErC,MAAM,IAAI,GAAG,kCAAkC,CAAC;IAChD,MAAM,YAAY,GAAG,eAAe,CAAC;IACrC,MAAM,QAAQ,GAAG,qBAAqB,CAAC;IACvC,MAAM,aAAa,GAAG,mCAAmC,CAAC;IAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,IAAI,QAAQ,CAAC;IAC/C,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,IAAI,QAAQ,CAAC;IACpE,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,IAAI,MAAM,CAAC;IAC9D,MAAM,cAAc,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IAE3D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,QAAQ,CAAC,IAAI,CACX,eAAe,KAAK,iFAAiF,CACtG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,GAAG,IAAI,+BAA+B;gBAC5C,OAAO,EAAE,oBAAoB,CAAC;oBAC5B,SAAS;oBACT,KAAK;oBACL,WAAW;oBACX,SAAS;oBACT,cAAc;oBACd,YAAY;iBACb,CAAC;aACH;YACD;gBACE,IAAI,EAAE,GAAG,IAAI,4BAA4B;gBACzC,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC;aACtC;YACD;gBACE,IAAI,EAAE,GAAG,IAAI,sCAAsC;gBACnD,OAAO,EAAE,0BAA0B,CAAC;oBAClC,SAAS;oBACT,KAAK;oBACL,WAAW;oBACX,SAAS;oBACT,cAAc;oBACd,YAAY;iBACb,CAAC;aACH;YACD,GAAG,CAAC,YAAY,CAAC,OAAO;gBACtB,CAAC,CAAC;oBACE;wBACE,IAAI,EAAE,GAAG,aAAa,oBAAoB;wBAC1C,OAAO,EAAE,gCAAgC,CAAC,YAAY,CAAC;qBACxD;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP;gBACE,IAAI,EAAE,GAAG,QAAQ,6BAA6B;gBAC9C,OAAO,EAAE,6BAA6B,CAAC,YAAY,CAAC;aACrD;SACF;QACD,SAAS,EAAE;YACT,GAAG,YAAY,+BAA+B;YAC9C,GAAG,YAAY,4BAA4B;YAC3C,GAAG,YAAY,sCAAsC;SACtD;QACD,UAAU,EAAE;YACV,mCAAmC;YACnC,eAAe;YACf,oBAAoB;YACpB,4BAA4B;YAC5B,sBAAsB;YACtB,2BAA2B;YAC3B,gCAAgC;SACjC;QACD,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAO7B;IACC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAExF,OAAO;;;;eAIM,SAAS;;;iBAGP,KAAK;mBACH,WAAW;iBACb,SAAS;+BACK,sBAAsB,CAAC,cAAc,CAAC,oBAAoB,CAAC;mCACvD,sBAAsB,CAAC,cAAc,CAAC,uBAAuB,CAAC;mCAC9D,sBAAsB,CAAC,cAAc,CAAC,uBAAuB,CAAC;0BACvE,cAAc,CAAC,YAAY;0BAC3B,sBAAsB,CAAC,cAAc,CAAC,gBAAgB,CAAC;yBACxD,sBAAsB,CAAC,YAAY,CAAC,KAAK,CAAC;+BACpC,YAAY,CAAC,UAAU;mCACnB,YAAY,CAAC,cAAc;mCAC3B,YAAY,CAAC,cAAc;CAC7D,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,OAAO;;;;eAIM,SAAS;;;;;;;;;CASvB,CAAC;AACF,CAAC;AAED,SAAS,0BAA0B,CAAC,IAOnC;IACC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAExF,OAAO;;;;eAIM,SAAS;;;;iBAIP,KAAK;mBACH,WAAW;iBACb,SAAS;+BACK,sBAAsB,CAAC,cAAc,CAAC,oBAAoB,CAAC;mCACvD,sBAAsB,CAAC,cAAc,CAAC,uBAAuB,CAAC;mCAC9D,sBAAsB,CAAC,cAAc,CAAC,uBAAuB,CAAC;0BACvE,cAAc,CAAC,YAAY;0BAC3B,sBAAsB,CAAC,cAAc,CAAC,gBAAgB,CAAC;yBACxD,sBAAsB,CAAC,YAAY,CAAC,KAAK,CAAC;+BACpC,YAAY,CAAC,UAAU;mCACnB,YAAY,CAAC,cAAc;mCAC3B,YAAY,CAAC,cAAc;;;CAG7D,CAAC;AACF,CAAC;AAED,SAAS,6BAA6B,CAAC,YAAkC;IACvE,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO;QACzC,CAAC,CAAC;;;;WAIK,YAAY,CAAC,KAAK,2BAA2B,YAAY,CAAC,KAAK;;;;;;;;;CASzE;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDP,cAAc;;;;;CAKf,CAAC;AACF,CAAC;AAED,MAAM,2BAA2B,GAAG,CAAC,OAAO,CAAC,CAAC;AAC9C,MAAM,+BAA+B,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC9D,MAAM,+BAA+B,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAClE,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAS5C,SAAS,qBAAqB,CAAC,QAA4B;IACzD,MAAM,iBAAiB,GAAG,kBAAkB,CAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAClC,2BAA2B,CAC5B,CAAC;IACF,MAAM,oBAAoB,GAAG,kBAAkB,CAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EACrC,+BAA+B,CAChC,CAAC;IACF,MAAM,oBAAoB,GAAG,kBAAkB,CAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EACrC,+BAA+B,CAChC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,IAAI,sBAAsB,CAAC;IACnF,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE;QACvE,OAAO;QACP,WAAW;QACX,UAAU;KACX,CAAC,CAAC;IAEH,OAAO;QACL,oBAAoB,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;QACjD,uBAAuB,EAAE,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;QACvD,uBAAuB,EAAE,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;QACvD,YAAY;QACZ,gBAAgB,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAqC,EACrC,QAA2B;IAE3B,MAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7C,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["import type { InfraManifestInput } from '../../../../types';\nimport type { MinikubeAdapterArtifacts } from '../../contracts';\nimport {\n getSupabaseProfileReconciliation,\n type ResolvedProfileModel,\n resolveSupabaseProfileModel,\n} from './profile';\n\nexport function generateSupabaseAuthArtifacts(args: {\n manifest: InfraManifestInput;\n namespace: string;\n}): MinikubeAdapterArtifacts {\n const { manifest, namespace } = args;\n\n const root = 'infra/minikube/k8s/auth/supabase';\n const resourceRoot = 'auth/supabase';\n const docsRoot = 'infra/minikube/auth';\n const generatedRoot = 'infra/minikube/supabase/generated';\n const scope = manifest.auth?.scope ?? 'global';\n const authzEngine = manifest.auth?.authorization.engine ?? 'native';\n const authzKind = manifest.auth?.authorization.kind ?? 'RBAC';\n const authFieldModel = resolveAuthFieldModel(manifest);\n const profileModel = resolveSupabaseProfileModel(manifest);\n\n const warnings: string[] = [];\n if (scope !== 'global') {\n warnings.push(\n `Auth scope \"${scope}\" is not fully modeled yet for Supabase. Baseline resources assume global auth.`,\n );\n }\n\n return {\n files: [\n {\n path: `${root}/supabase-auth.configmap.yaml`,\n content: getSupabaseConfigMap({\n namespace,\n scope,\n authzEngine,\n authzKind,\n authFieldModel,\n profileModel,\n }),\n },\n {\n path: `${root}/supabase-auth.secret.yaml`,\n content: getSupabaseSecret(namespace),\n },\n {\n path: `${root}/app-runtime-auth.env.configmap.yaml`,\n content: getRuntimeAuthEnvConfigMap({\n namespace,\n scope,\n authzEngine,\n authzKind,\n authFieldModel,\n profileModel,\n }),\n },\n ...(profileModel.enabled\n ? [\n {\n path: `${generatedRoot}/auth_profiles.sql`,\n content: getSupabaseProfileReconciliation(profileModel),\n },\n ]\n : []),\n {\n path: `${docsRoot}/supabase-runtime-wiring.md`,\n content: getSupabaseRuntimeWiringGuide(profileModel),\n },\n ],\n resources: [\n `${resourceRoot}/supabase-auth.configmap.yaml`,\n `${resourceRoot}/supabase-auth.secret.yaml`,\n `${resourceRoot}/app-runtime-auth.env.configmap.yaml`,\n ],\n envEntries: [\n 'SUPABASE_SECRET_SYNC_ENABLED=true',\n 'SUPABASE_URL=',\n 'SUPABASE_ANON_KEY=',\n 'SUPABASE_SERVICE_ROLE_KEY=',\n 'SUPABASE_JWT_SECRET=',\n 'EXPO_PUBLIC_SUPABASE_URL=',\n 'EXPO_PUBLIC_SUPABASE_ANON_KEY=',\n ],\n warnings,\n };\n}\n\nfunction getSupabaseConfigMap(args: {\n namespace: string;\n scope: string;\n authzEngine: string;\n authzKind: string;\n authFieldModel: ResolvedAuthFieldModel;\n profileModel: ResolvedProfileModel;\n}) {\n const { namespace, scope, authzEngine, authzKind, authFieldModel, profileModel } = args;\n\n return `apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: supabase-auth-config\n namespace: ${namespace}\ndata:\n AUTH_PROVIDER: \"supabase\"\n AUTH_SCOPE: \"${scope}\"\n AUTHZ_ENGINE: \"${authzEngine}\"\n AUTHZ_KIND: \"${authzKind}\"\n AUTH_SIGN_IN_IDENTIFIERS: \"${escapeYamlDoubleQuoted(authFieldModel.signInIdentifiersCsv)}\"\n AUTH_SIGN_UP_REQUIRED_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpRequiredFieldsCsv)}\"\n AUTH_SIGN_UP_OPTIONAL_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpOptionalFieldsCsv)}\"\n AUTH_SIGN_UP_POLICY: \"${authFieldModel.signUpPolicy}\"\n AUTH_PROFILE_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.profileFieldsCsv)}\"\n AUTH_PROFILE_TABLE: \"${escapeYamlDoubleQuoted(profileModel.table)}\"\n AUTH_PROFILE_PRIMARY_KEY: \"${profileModel.primaryKey}\"\n AUTH_PROFILE_CREATE_STRATEGY: \"${profileModel.createStrategy}\"\n AUTH_PROFILE_UPDATE_STRATEGY: \"${profileModel.updateStrategy}\"\n`;\n}\n\nfunction getSupabaseSecret(namespace: string) {\n return `apiVersion: v1\nkind: Secret\nmetadata:\n name: supabase-auth-secrets\n namespace: ${namespace}\ntype: Opaque\nstringData:\n SUPABASE_URL: \"\"\n SUPABASE_ANON_KEY: \"\"\n SUPABASE_SERVICE_ROLE_KEY: \"\"\n SUPABASE_JWT_SECRET: \"\"\n EXPO_PUBLIC_SUPABASE_URL: \"\"\n EXPO_PUBLIC_SUPABASE_ANON_KEY: \"\"\n`;\n}\n\nfunction getRuntimeAuthEnvConfigMap(args: {\n namespace: string;\n scope: string;\n authzEngine: string;\n authzKind: string;\n authFieldModel: ResolvedAuthFieldModel;\n profileModel: ResolvedProfileModel;\n}) {\n const { namespace, scope, authzEngine, authzKind, authFieldModel, profileModel } = args;\n\n return `apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: app-runtime-auth-env\n namespace: ${namespace}\ndata:\n AUTH_REQUIRED: \"true\"\n AUTH_PROVIDER: \"supabase\"\n AUTH_SCOPE: \"${scope}\"\n AUTHZ_ENGINE: \"${authzEngine}\"\n AUTHZ_KIND: \"${authzKind}\"\n AUTH_SIGN_IN_IDENTIFIERS: \"${escapeYamlDoubleQuoted(authFieldModel.signInIdentifiersCsv)}\"\n AUTH_SIGN_UP_REQUIRED_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpRequiredFieldsCsv)}\"\n AUTH_SIGN_UP_OPTIONAL_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.signUpOptionalFieldsCsv)}\"\n AUTH_SIGN_UP_POLICY: \"${authFieldModel.signUpPolicy}\"\n AUTH_PROFILE_FIELDS: \"${escapeYamlDoubleQuoted(authFieldModel.profileFieldsCsv)}\"\n AUTH_PROFILE_TABLE: \"${escapeYamlDoubleQuoted(profileModel.table)}\"\n AUTH_PROFILE_PRIMARY_KEY: \"${profileModel.primaryKey}\"\n AUTH_PROFILE_CREATE_STRATEGY: \"${profileModel.createStrategy}\"\n AUTH_PROFILE_UPDATE_STRATEGY: \"${profileModel.updateStrategy}\"\n AUTH_TOKEN_HEADER: \"Authorization\"\n AUTH_TOKEN_PREFIX: \"Bearer\"\n`;\n}\n\nfunction getSupabaseRuntimeWiringGuide(profileModel: ResolvedProfileModel) {\n const profileSection = profileModel.enabled\n ? `\n## App Profile Table\n\nIdentity remains provider-owned in \\`auth.users\\`. App-facing profile data is generated in\n\\`public.${profileModel.table}\\` and linked through \\`${profileModel.table}.id -> auth.users.id\\`.\n\nThe generated profile reconciliation:\n\n- creates the profile table\n- enables row-level security\n- allows signed-in users to read and update their own profile row\n- drops stale Ankhorage-managed profile columns that are no longer configured\n- creates a trigger for new auth users when \\`AUTH_PROFILE_CREATE_STRATEGY=trigger\\`\n`\n : '';\n\n return `# Supabase Runtime Wiring\n\nThis file documents the runtime conventions generated by the minikube Supabase adapter.\n\n## Generated Kubernetes Objects\n\n- \\`ConfigMap/supabase-auth-config\\`: high-level auth metadata.\n- \\`Secret/supabase-auth-secrets\\`: Supabase keys and public runtime keys.\n- \\`ConfigMap/app-runtime-auth-env\\`: auth runtime feature flags and authz metadata.\n\n## App Runtime Convention\n\nYour app runtime deployment should import both:\n\n- \\`app-runtime-auth-env\\` (configMap)\n- \\`supabase-auth-secrets\\` (secret)\n\nExample deployment fragment:\n\n\\`\\`\\`yaml\nenvFrom:\n - configMapRef:\n name: app-runtime-auth-env\n - secretRef:\n name: supabase-auth-secrets\n\\`\\`\\`\n\nThe generated base deployment at \\`k8s/app/deployment.yaml\\` already includes this wiring\nwith \\`optional: true\\` for auth sources.\n\n## Expected Environment Variables\n\n- \\`AUTH_REQUIRED\\`\n- \\`AUTH_PROVIDER\\`\n- \\`AUTH_SCOPE\\`\n- \\`AUTHZ_ENGINE\\`\n- \\`AUTHZ_KIND\\`\n- \\`AUTH_SIGN_IN_IDENTIFIERS\\`\n- \\`AUTH_SIGN_UP_REQUIRED_FIELDS\\`\n- \\`AUTH_SIGN_UP_OPTIONAL_FIELDS\\`\n- \\`AUTH_SIGN_UP_POLICY\\`\n- \\`AUTH_PROFILE_FIELDS\\`\n- \\`AUTH_PROFILE_TABLE\\`\n- \\`AUTH_PROFILE_PRIMARY_KEY\\`\n- \\`AUTH_PROFILE_CREATE_STRATEGY\\`\n- \\`AUTH_PROFILE_UPDATE_STRATEGY\\`\n- \\`SUPABASE_SECRET_SYNC_ENABLED\\`\n- \\`SUPABASE_URL\\`\n- \\`SUPABASE_ANON_KEY\\`\n- \\`SUPABASE_SERVICE_ROLE_KEY\\`\n- \\`SUPABASE_JWT_SECRET\\`\n- \\`EXPO_PUBLIC_SUPABASE_URL\\`\n- \\`EXPO_PUBLIC_SUPABASE_ANON_KEY\\`\n${profileSection}\n## Secret Sync\n\nWhen \\`SUPABASE_SECRET_SYNC_ENABLED=true\\`, \\`scripts/up.sh\\` will create/update\n\\`Secret/supabase-auth-secrets\\` from the loaded environment values.\n`;\n}\n\nconst DEFAULT_SIGN_IN_IDENTIFIERS = ['email'];\nconst DEFAULT_SIGN_UP_REQUIRED_FIELDS = ['email', 'password'];\nconst DEFAULT_SIGN_UP_OPTIONAL_FIELDS = ['firstName', 'lastName'];\nconst DEFAULT_SIGN_UP_POLICY = 'autoSignIn';\ninterface ResolvedAuthFieldModel {\n signInIdentifiersCsv: string;\n signUpRequiredFieldsCsv: string;\n signUpOptionalFieldsCsv: string;\n signUpPolicy: string;\n profileFieldsCsv: string;\n}\n\nfunction resolveAuthFieldModel(manifest: InfraManifestInput): ResolvedAuthFieldModel {\n const signInIdentifiers = normalizeFieldList(\n manifest.auth?.signIn?.identifiers,\n DEFAULT_SIGN_IN_IDENTIFIERS,\n );\n const signUpRequiredFields = normalizeFieldList(\n manifest.auth?.signUp?.requiredFields,\n DEFAULT_SIGN_UP_REQUIRED_FIELDS,\n );\n const signUpOptionalFields = normalizeFieldList(\n manifest.auth?.signUp?.optionalFields,\n DEFAULT_SIGN_UP_OPTIONAL_FIELDS,\n ).filter((field) => !signUpRequiredFields.includes(field));\n const signUpPolicy = manifest.auth?.signUp?.signUpPolicy ?? DEFAULT_SIGN_UP_POLICY;\n const profileFields = normalizeFieldList(manifest.auth?.profile?.fields, [\n 'email',\n 'firstName',\n 'lastName',\n ]);\n\n return {\n signInIdentifiersCsv: signInIdentifiers.join(','),\n signUpRequiredFieldsCsv: signUpRequiredFields.join(','),\n signUpOptionalFieldsCsv: signUpOptionalFields.join(','),\n signUpPolicy,\n profileFieldsCsv: profileFields.join(','),\n };\n}\n\nfunction normalizeFieldList(\n values: readonly string[] | undefined,\n fallback: readonly string[],\n): string[] {\n const source = values ?? fallback;\n const next: string[] = [];\n\n for (const value of source) {\n const normalized = value.trim();\n if (!normalized || next.includes(normalized)) {\n continue;\n }\n next.push(normalized);\n }\n\n if (next.length > 0) {\n return next;\n }\n\n return [...fallback];\n}\n\nfunction escapeYamlDoubleQuoted(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"');\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { InfraManifestInput } from '../../../../types';
2
+ export declare const MANAGED_PROFILE_COLUMNS: readonly ["email", "display_name", "first_name", "last_name", "avatar_url", "username", "phone"];
3
+ interface ProfileColumnSpec {
4
+ readonly field: string;
5
+ readonly column: string;
6
+ readonly sqlType: string;
7
+ readonly fromNewUser: string;
8
+ }
9
+ export interface ResolvedProfileModel {
10
+ readonly enabled: boolean;
11
+ readonly table: string;
12
+ readonly primaryKey: string;
13
+ readonly createStrategy: string;
14
+ readonly updateStrategy: string;
15
+ readonly columns: readonly ProfileColumnSpec[];
16
+ readonly desiredStateHash: string;
17
+ }
18
+ export declare function resolveSupabaseProfileModel(manifest: InfraManifestInput): ResolvedProfileModel;
19
+ export declare function getSupabaseProfileReconciliation(profileModel: ResolvedProfileModel): string;
20
+ export {};
21
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../../../../src/adapters/minikube/auth/supabase/profile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAU5D,eAAO,MAAM,uBAAuB,kGAQ1B,CAAC;AAEX,UAAU,iBAAiB;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC/C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,kBAAkB,GAAG,oBAAoB,CA2B9F;AAED,wBAAgB,gCAAgC,CAAC,YAAY,EAAE,oBAAoB,GAAG,MAAM,CAoT3F"}