@ankhorage/infra 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/infra
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bd82fe0: Add canonical environment-aware Auth redirect configuration and reconcile Minikube GoTrue callback settings through a forced, bounded deployment rollout.
8
+
9
+ ## 1.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - 296d006: update SUPABASE-VAULT
14
+
3
15
  ## 1.0.5
4
16
 
5
17
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAA0B,KAAK,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAGrF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,wBAAwB,CAe3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAA0B,KAAK,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAIrF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,wBAAwB,CA2B3B"}
@@ -1,4 +1,5 @@
1
1
  import { emptyMinikubeArtifacts } from '../contracts';
2
+ import { generateSupabaseOAuthRuntimeArtifacts } from './oauthRuntime';
2
3
  import { generateSupabaseAuthArtifacts } from './supabase';
3
4
  export function generateAuthProviderArtifacts(args) {
4
5
  const { manifest, namespace } = args;
@@ -9,6 +10,17 @@ export function generateAuthProviderArtifacts(args) {
9
10
  if (auth.provider !== 'supabase') {
10
11
  throw new Error(`Unsupported auth provider for minikube adapter: "${auth.provider}". Only "supabase" is currently supported.`);
11
12
  }
12
- return generateSupabaseAuthArtifacts({ manifest, namespace });
13
+ const providerArtifacts = generateSupabaseAuthArtifacts({ manifest, namespace });
14
+ const oauthArtifacts = generateSupabaseOAuthRuntimeArtifacts(manifest);
15
+ return {
16
+ files: [...providerArtifacts.files, ...oauthArtifacts.files],
17
+ resources: [...providerArtifacts.resources, ...oauthArtifacts.resources],
18
+ providerLifecycle: [
19
+ ...providerArtifacts.providerLifecycle,
20
+ ...oauthArtifacts.providerLifecycle,
21
+ ],
22
+ envEntries: [...providerArtifacts.envEntries, ...oauthArtifacts.envEntries],
23
+ warnings: [...providerArtifacts.warnings, ...oauthArtifacts.warnings],
24
+ };
13
25
  }
14
26
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAiC,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,UAAU,6BAA6B,CAAC,IAG7C;IACC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAE1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,sBAAsB,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,QAAQ,4CAA4C,CAC9G,CAAC;IACJ,CAAC;IAED,OAAO,6BAA6B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AAChE,CAAC","sourcesContent":["import type { InfraManifestInput } from '../../../types';\nimport { emptyMinikubeArtifacts, type MinikubeAdapterArtifacts } from '../contracts';\nimport { generateSupabaseAuthArtifacts } from './supabase';\n\nexport function generateAuthProviderArtifacts(args: {\n manifest: InfraManifestInput;\n namespace: string;\n}): MinikubeAdapterArtifacts {\n const { manifest, namespace } = args;\n const { auth } = manifest;\n\n if (!auth) {\n return emptyMinikubeArtifacts();\n }\n\n if (auth.provider !== 'supabase') {\n throw new Error(\n `Unsupported auth provider for minikube adapter: \"${auth.provider}\". Only \"supabase\" is currently supported.`,\n );\n }\n\n return generateSupabaseAuthArtifacts({ manifest, namespace });\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAiC,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,qCAAqC,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,UAAU,6BAA6B,CAAC,IAG7C;IACC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAE1B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,sBAAsB,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,QAAQ,4CAA4C,CAC9G,CAAC;IACJ,CAAC;IAED,MAAM,iBAAiB,GAAG,6BAA6B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IACjF,MAAM,cAAc,GAAG,qCAAqC,CAAC,QAAQ,CAAC,CAAC;IAEvE,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,iBAAiB,CAAC,KAAK,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC;QAC5D,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,SAAS,EAAE,GAAG,cAAc,CAAC,SAAS,CAAC;QACxE,iBAAiB,EAAE;YACjB,GAAG,iBAAiB,CAAC,iBAAiB;YACtC,GAAG,cAAc,CAAC,iBAAiB;SACpC;QACD,UAAU,EAAE,CAAC,GAAG,iBAAiB,CAAC,UAAU,EAAE,GAAG,cAAc,CAAC,UAAU,CAAC;QAC3E,QAAQ,EAAE,CAAC,GAAG,iBAAiB,CAAC,QAAQ,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC;KACtE,CAAC;AACJ,CAAC","sourcesContent":["import type { InfraManifestInput } from '../../../types';\nimport { emptyMinikubeArtifacts, type MinikubeAdapterArtifacts } from '../contracts';\nimport { generateSupabaseOAuthRuntimeArtifacts } from './oauthRuntime';\nimport { generateSupabaseAuthArtifacts } from './supabase';\n\nexport function generateAuthProviderArtifacts(args: {\n manifest: InfraManifestInput;\n namespace: string;\n}): MinikubeAdapterArtifacts {\n const { manifest, namespace } = args;\n const { auth } = manifest;\n\n if (!auth) {\n return emptyMinikubeArtifacts();\n }\n\n if (auth.provider !== 'supabase') {\n throw new Error(\n `Unsupported auth provider for minikube adapter: \"${auth.provider}\". Only \"supabase\" is currently supported.`,\n );\n }\n\n const providerArtifacts = generateSupabaseAuthArtifacts({ manifest, namespace });\n const oauthArtifacts = generateSupabaseOAuthRuntimeArtifacts(manifest);\n\n return {\n files: [...providerArtifacts.files, ...oauthArtifacts.files],\n resources: [...providerArtifacts.resources, ...oauthArtifacts.resources],\n providerLifecycle: [\n ...providerArtifacts.providerLifecycle,\n ...oauthArtifacts.providerLifecycle,\n ],\n envEntries: [...providerArtifacts.envEntries, ...oauthArtifacts.envEntries],\n warnings: [...providerArtifacts.warnings, ...oauthArtifacts.warnings],\n };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { InfraManifestInput } from '../../../types';
2
+ import type { MinikubeAdapterArtifacts } from '../contracts';
3
+ export declare function generateSupabaseOAuthRuntimeArtifacts(manifest: InfraManifestInput): MinikubeAdapterArtifacts;
4
+ //# sourceMappingURL=oauthRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthRuntime.d.ts","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/oauthRuntime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,wBAAwB,EAA6B,MAAM,cAAc,CAAC;AAQxF,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,kBAAkB,GAC3B,wBAAwB,CAwB1B"}
@@ -0,0 +1,144 @@
1
+ import { getLocalAuthRedirectPatterns, normalizeAuthCallbackRoute } from '../../../authRedirects';
2
+ export function generateSupabaseOAuthRuntimeArtifacts(manifest) {
3
+ const oauthRuntime = resolveOAuthRuntimeModel(manifest);
4
+ if (!oauthRuntime) {
5
+ return {
6
+ files: [],
7
+ resources: [],
8
+ providerLifecycle: [],
9
+ envEntries: [],
10
+ warnings: [],
11
+ };
12
+ }
13
+ return {
14
+ files: [
15
+ {
16
+ path: 'infra/minikube/auth/oauth-runtime.md',
17
+ content: getOAuthRuntimeGuide(oauthRuntime),
18
+ },
19
+ ],
20
+ resources: [],
21
+ providerLifecycle: [getOAuthProviderLifecycle(oauthRuntime)],
22
+ envEntries: [],
23
+ warnings: [],
24
+ };
25
+ }
26
+ function resolveOAuthRuntimeModel(manifest) {
27
+ const oauth = manifest.auth?.oauth;
28
+ if (!oauth?.enabled)
29
+ return null;
30
+ const providers = oauth.providers.filter((provider) => provider.enabled !== false);
31
+ if (providers.length === 0)
32
+ return null;
33
+ const callbackRoute = normalizeAuthCallbackRoute(oauth.callbackRoute);
34
+ const envPrefixes = providers.map((provider) => {
35
+ const prefix = provider.id
36
+ .trim()
37
+ .toUpperCase()
38
+ .replace(/[^A-Z0-9]+/gu, '_')
39
+ .replace(/^_+|_+$/gu, '');
40
+ if (!prefix) {
41
+ throw new Error(`OAuth provider "${provider.id}" cannot be mapped to GoTrue environment keys.`);
42
+ }
43
+ return prefix;
44
+ });
45
+ return {
46
+ callbackRoute,
47
+ envPrefixes,
48
+ localRedirectPatterns: getLocalAuthRedirectPatterns(callbackRoute),
49
+ };
50
+ }
51
+ function getOAuthProviderLifecycle(oauthRuntime) {
52
+ return {
53
+ id: 'supabase-auth',
54
+ namespace: 'supabase',
55
+ endpoints: [],
56
+ readinessChecks: [
57
+ {
58
+ label: 'GoTrue',
59
+ namespace: 'supabase',
60
+ resource: 'deployment/auth',
61
+ timeoutSeconds: 600,
62
+ },
63
+ ],
64
+ migrationCommands: [],
65
+ reconciliationCommands: [
66
+ {
67
+ label: 'OAuth redirect and runtime rollout reconciliation',
68
+ command: getOAuthRuntimeReconciliationCommand(oauthRuntime),
69
+ },
70
+ ],
71
+ statusChecks: [
72
+ {
73
+ label: 'OAuth redirect configuration',
74
+ command: getOAuthRuntimeStatusCommand(oauthRuntime),
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ function getOAuthRuntimeReconciliationCommand(oauthRuntime) {
80
+ const localPatterns = oauthRuntime.localRedirectPatterns.join(',');
81
+ const providerRedirectAssignments = oauthRuntime.envPrefixes
82
+ .map((prefix) => `GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI="\${oauth_provider_callback}"`)
83
+ .join(' ');
84
+ const providerExports = oauthRuntime.envPrefixes
85
+ .map((prefix) => `export GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI="\${oauth_provider_callback}"
86
+ write_env_value GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI "\${oauth_provider_callback}"`)
87
+ .join('\n');
88
+ return `oauth_callback_route='${oauthRuntime.callbackRoute}'
89
+ oauth_callback_path="\${oauth_callback_route#/}"
90
+ oauth_site_url="\${SITE_URL%/}"
91
+ oauth_provider_callback="\${API_EXTERNAL_URL%/}/callback"
92
+ oauth_redirect_allow_list="\${oauth_site_url},\${oauth_site_url}/\${oauth_callback_path},${localPatterns}"
93
+ if [[ -n "\${OAUTH_NATIVE_REDIRECT_URLS:-}" ]]; then
94
+ oauth_redirect_allow_list="\${oauth_redirect_allow_list},\${OAUTH_NATIVE_REDIRECT_URLS}"
95
+ fi
96
+ export ADDITIONAL_REDIRECT_URLS="\${oauth_redirect_allow_list}"
97
+ write_env_value ADDITIONAL_REDIRECT_URLS "\${oauth_redirect_allow_list}"
98
+ ${providerExports}
99
+ kubectl --context "\${PROFILE}" -n supabase set env deployment/auth \\
100
+ API_EXTERNAL_URL="\${API_EXTERNAL_URL}" \\
101
+ GOTRUE_SITE_URL="\${SITE_URL}" \\
102
+ GOTRUE_URI_ALLOW_LIST="\${oauth_redirect_allow_list}" \\
103
+ GOTRUE_JWT_ISSUER="\${API_EXTERNAL_URL}" \\
104
+ ${providerRedirectAssignments} >/dev/null
105
+ kubectl --context "\${PROFILE}" -n supabase rollout restart deployment/auth >/dev/null
106
+ kubectl --context "\${PROFILE}" -n supabase rollout status deployment/auth --timeout=600s`;
107
+ }
108
+ function getOAuthRuntimeStatusCommand(oauthRuntime) {
109
+ return `oauth_callback_route='${oauthRuntime.callbackRoute}'
110
+ oauth_callback_path="\${oauth_callback_route#/}"
111
+ if [[ -n "\${API_EXTERNAL_URL:-}" ]]; then
112
+ echo "- provider supabase-auth/provider-callback: \${API_EXTERNAL_URL%/}/callback"
113
+ else
114
+ echo "- provider supabase-auth/provider-callback: unavailable"
115
+ fi
116
+ if [[ -n "\${SITE_URL:-}" ]]; then
117
+ echo "- provider supabase-auth/app-callback: \${SITE_URL%/}/\${oauth_callback_path}"
118
+ else
119
+ echo "- provider supabase-auth/app-callback: unavailable"
120
+ fi
121
+ echo "- provider supabase-auth/local-callback-patterns: ${oauthRuntime.localRedirectPatterns.join(',')}"`;
122
+ }
123
+ function getOAuthRuntimeGuide(oauthRuntime) {
124
+ const localPatterns = oauthRuntime.localRedirectPatterns
125
+ .map((pattern) => `- \`${pattern}\``)
126
+ .join('\n');
127
+ return `# Supabase OAuth Runtime Reconciliation
128
+
129
+ The provider callback is derived from the active project gateway as
130
+ \`\${API_EXTERNAL_URL%/}/callback\`, which resolves to the project-owned
131
+ \`/auth/v1/callback\` endpoint. GoTrue-to-app redirects use the configured callback route
132
+ \`${oauthRuntime.callbackRoute}\`.
133
+
134
+ Local Minikube reconciliation adds only callback-scoped loopback patterns:
135
+
136
+ ${localPatterns}
137
+
138
+ Native callback URIs may be supplied through \`OAUTH_NATIVE_REDIRECT_URLS\`. The generated
139
+ provider lifecycle writes only redirect metadata, never OAuth client secrets. It updates the
140
+ GoTrue deployment environment, forces \`deployment/auth\` to restart, and waits up to 600
141
+ seconds for the rollout. A failed rollout stops Infra Up before its success message.
142
+ `;
143
+ }
144
+ //# sourceMappingURL=oauthRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthRuntime.js","sourceRoot":"","sources":["../../../../src/adapters/minikube/auth/oauthRuntime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAUlG,MAAM,UAAU,qCAAqC,CACnD,QAA4B;IAE5B,MAAM,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;YACL,KAAK,EAAE,EAAE;YACT,SAAS,EAAE,EAAE;YACb,iBAAiB,EAAE,EAAE;YACrB,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;SACb,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,sCAAsC;gBAC5C,OAAO,EAAE,oBAAoB,CAAC,YAAY,CAAC;aAC5C;SACF;QACD,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,CAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC;QAC5D,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,QAA4B;IAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;IACnF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,aAAa,GAAG,0BAA0B,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACtE,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE;aACvB,IAAI,EAAE;aACN,WAAW,EAAE;aACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;aAC5B,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,mBAAmB,QAAQ,CAAC,EAAE,gDAAgD,CAC/E,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,aAAa;QACb,WAAW;QACX,qBAAqB,EAAE,4BAA4B,CAAC,aAAa,CAAC;KACnE,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,YAAuC;IAEvC,OAAO;QACL,EAAE,EAAE,eAAe;QACnB,SAAS,EAAE,UAAU;QACrB,SAAS,EAAE,EAAE;QACb,eAAe,EAAE;YACf;gBACE,KAAK,EAAE,QAAQ;gBACf,SAAS,EAAE,UAAU;gBACrB,QAAQ,EAAE,iBAAiB;gBAC3B,cAAc,EAAE,GAAG;aACpB;SACF;QACD,iBAAiB,EAAE,EAAE;QACrB,sBAAsB,EAAE;YACtB;gBACE,KAAK,EAAE,mDAAmD;gBAC1D,OAAO,EAAE,oCAAoC,CAAC,YAAY,CAAC;aAC5D;SACF;QACD,YAAY,EAAE;YACZ;gBACE,KAAK,EAAE,8BAA8B;gBACrC,OAAO,EAAE,4BAA4B,CAAC,YAAY,CAAC;aACpD;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,oCAAoC,CAAC,YAAuC;IACnF,MAAM,aAAa,GAAG,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnE,MAAM,2BAA2B,GAAG,YAAY,CAAC,WAAW;SACzD,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,MAAM,6CAA6C,CAAC;SACvF,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,eAAe,GAAG,YAAY,CAAC,WAAW;SAC7C,GAAG,CACF,CAAC,MAAM,EAAE,EAAE,CAAC,0BAA0B,MAAM;kCAChB,MAAM,6CAA6C,CAChF;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,yBAAyB,YAAY,CAAC,aAAa;;;;2FAI+B,aAAa;;;;;;EAMtG,eAAe;;;;;;IAMb,2BAA2B;;0FAE2D,CAAC;AAC3F,CAAC;AAED,SAAS,4BAA4B,CAAC,YAAuC;IAC3E,OAAO,yBAAyB,YAAY,CAAC,aAAa;;;;;;;;;;;;0DAYF,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAC1G,CAAC;AAED,SAAS,oBAAoB,CAAC,YAAuC;IACnE,MAAM,aAAa,GAAG,YAAY,CAAC,qBAAqB;SACrD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,OAAO,IAAI,CAAC;SACpC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;;;;;IAKL,YAAY,CAAC,aAAa;;;;EAI5B,aAAa;;;;;;CAMd,CAAC;AACF,CAAC","sourcesContent":["import { getLocalAuthRedirectPatterns, normalizeAuthCallbackRoute } from '../../../authRedirects';\nimport type { InfraManifestInput } from '../../../types';\nimport type { MinikubeAdapterArtifacts, MinikubeProviderLifecycle } from '../contracts';\n\ninterface SupabaseOAuthRuntimeModel {\n callbackRoute: string;\n envPrefixes: readonly string[];\n localRedirectPatterns: readonly string[];\n}\n\nexport function generateSupabaseOAuthRuntimeArtifacts(\n manifest: InfraManifestInput,\n): MinikubeAdapterArtifacts {\n const oauthRuntime = resolveOAuthRuntimeModel(manifest);\n if (!oauthRuntime) {\n return {\n files: [],\n resources: [],\n providerLifecycle: [],\n envEntries: [],\n warnings: [],\n };\n }\n\n return {\n files: [\n {\n path: 'infra/minikube/auth/oauth-runtime.md',\n content: getOAuthRuntimeGuide(oauthRuntime),\n },\n ],\n resources: [],\n providerLifecycle: [getOAuthProviderLifecycle(oauthRuntime)],\n envEntries: [],\n warnings: [],\n };\n}\n\nfunction resolveOAuthRuntimeModel(manifest: InfraManifestInput): SupabaseOAuthRuntimeModel | null {\n const oauth = manifest.auth?.oauth;\n if (!oauth?.enabled) return null;\n\n const providers = oauth.providers.filter((provider) => provider.enabled !== false);\n if (providers.length === 0) return null;\n\n const callbackRoute = normalizeAuthCallbackRoute(oauth.callbackRoute);\n const envPrefixes = providers.map((provider) => {\n const prefix = provider.id\n .trim()\n .toUpperCase()\n .replace(/[^A-Z0-9]+/gu, '_')\n .replace(/^_+|_+$/gu, '');\n if (!prefix) {\n throw new Error(\n `OAuth provider \"${provider.id}\" cannot be mapped to GoTrue environment keys.`,\n );\n }\n return prefix;\n });\n\n return {\n callbackRoute,\n envPrefixes,\n localRedirectPatterns: getLocalAuthRedirectPatterns(callbackRoute),\n };\n}\n\nfunction getOAuthProviderLifecycle(\n oauthRuntime: SupabaseOAuthRuntimeModel,\n): MinikubeProviderLifecycle {\n return {\n id: 'supabase-auth',\n namespace: 'supabase',\n endpoints: [],\n readinessChecks: [\n {\n label: 'GoTrue',\n namespace: 'supabase',\n resource: 'deployment/auth',\n timeoutSeconds: 600,\n },\n ],\n migrationCommands: [],\n reconciliationCommands: [\n {\n label: 'OAuth redirect and runtime rollout reconciliation',\n command: getOAuthRuntimeReconciliationCommand(oauthRuntime),\n },\n ],\n statusChecks: [\n {\n label: 'OAuth redirect configuration',\n command: getOAuthRuntimeStatusCommand(oauthRuntime),\n },\n ],\n };\n}\n\nfunction getOAuthRuntimeReconciliationCommand(oauthRuntime: SupabaseOAuthRuntimeModel): string {\n const localPatterns = oauthRuntime.localRedirectPatterns.join(',');\n const providerRedirectAssignments = oauthRuntime.envPrefixes\n .map((prefix) => `GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI=\"\\${oauth_provider_callback}\"`)\n .join(' ');\n const providerExports = oauthRuntime.envPrefixes\n .map(\n (prefix) => `export GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI=\"\\${oauth_provider_callback}\"\nwrite_env_value GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI \"\\${oauth_provider_callback}\"`,\n )\n .join('\\n');\n\n return `oauth_callback_route='${oauthRuntime.callbackRoute}'\noauth_callback_path=\"\\${oauth_callback_route#/}\"\noauth_site_url=\"\\${SITE_URL%/}\"\noauth_provider_callback=\"\\${API_EXTERNAL_URL%/}/callback\"\noauth_redirect_allow_list=\"\\${oauth_site_url},\\${oauth_site_url}/\\${oauth_callback_path},${localPatterns}\"\nif [[ -n \"\\${OAUTH_NATIVE_REDIRECT_URLS:-}\" ]]; then\n oauth_redirect_allow_list=\"\\${oauth_redirect_allow_list},\\${OAUTH_NATIVE_REDIRECT_URLS}\"\nfi\nexport ADDITIONAL_REDIRECT_URLS=\"\\${oauth_redirect_allow_list}\"\nwrite_env_value ADDITIONAL_REDIRECT_URLS \"\\${oauth_redirect_allow_list}\"\n${providerExports}\nkubectl --context \"\\${PROFILE}\" -n supabase set env deployment/auth \\\\\n API_EXTERNAL_URL=\"\\${API_EXTERNAL_URL}\" \\\\\n GOTRUE_SITE_URL=\"\\${SITE_URL}\" \\\\\n GOTRUE_URI_ALLOW_LIST=\"\\${oauth_redirect_allow_list}\" \\\\\n GOTRUE_JWT_ISSUER=\"\\${API_EXTERNAL_URL}\" \\\\\n ${providerRedirectAssignments} >/dev/null\nkubectl --context \"\\${PROFILE}\" -n supabase rollout restart deployment/auth >/dev/null\nkubectl --context \"\\${PROFILE}\" -n supabase rollout status deployment/auth --timeout=600s`;\n}\n\nfunction getOAuthRuntimeStatusCommand(oauthRuntime: SupabaseOAuthRuntimeModel): string {\n return `oauth_callback_route='${oauthRuntime.callbackRoute}'\noauth_callback_path=\"\\${oauth_callback_route#/}\"\nif [[ -n \"\\${API_EXTERNAL_URL:-}\" ]]; then\n echo \"- provider supabase-auth/provider-callback: \\${API_EXTERNAL_URL%/}/callback\"\nelse\n echo \"- provider supabase-auth/provider-callback: unavailable\"\nfi\nif [[ -n \"\\${SITE_URL:-}\" ]]; then\n echo \"- provider supabase-auth/app-callback: \\${SITE_URL%/}/\\${oauth_callback_path}\"\nelse\n echo \"- provider supabase-auth/app-callback: unavailable\"\nfi\necho \"- provider supabase-auth/local-callback-patterns: ${oauthRuntime.localRedirectPatterns.join(',')}\"`;\n}\n\nfunction getOAuthRuntimeGuide(oauthRuntime: SupabaseOAuthRuntimeModel): string {\n const localPatterns = oauthRuntime.localRedirectPatterns\n .map((pattern) => `- \\`${pattern}\\``)\n .join('\\n');\n\n return `# Supabase OAuth Runtime Reconciliation\n\nThe provider callback is derived from the active project gateway as\n\\`\\${API_EXTERNAL_URL%/}/callback\\`, which resolves to the project-owned\n\\`/auth/v1/callback\\` endpoint. GoTrue-to-app redirects use the configured callback route\n\\`${oauthRuntime.callbackRoute}\\`.\n\nLocal Minikube reconciliation adds only callback-scoped loopback patterns:\n\n${localPatterns}\n\nNative callback URIs may be supplied through \\`OAUTH_NATIVE_REDIRECT_URLS\\`. The generated\nprovider lifecycle writes only redirect metadata, never OAuth client secrets. It updates the\nGoTrue deployment environment, forces \\`deployment/auth\\` to restart, and waits up to 600\nseconds for the rollout. A failed rollout stops Infra Up before its success message.\n`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AASrB,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAqFhC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AAUrB,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAqFhC"}
@@ -5,6 +5,7 @@ import { APP_NAMESPACE, generateMinikubeBaseArtifacts } from './base';
5
5
  import { generateSecretStoreArtifacts } from './secrets';
6
6
  import { generateStorageArtifacts } from './storage';
7
7
  const CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
8
+ const BUILT_IN_NAMESPACES = new Set([APP_NAMESPACE, 'supabase']);
8
9
  export function generateMinikubeInfra(manifest, options = {}) {
9
10
  if (manifest.deployment?.target !== 'minikube') {
10
11
  throw new Error(`Minikube adapter only supports deployment target "minikube" (received "${manifest.deployment?.target ?? 'undefined'}").`);
@@ -40,7 +41,7 @@ export function generateMinikubeInfra(manifest, options = {}) {
40
41
  ];
41
42
  const providerNamespaces = unique([
42
43
  ...providerLifecycle.map((contribution) => contribution.namespace),
43
- ]);
44
+ ]).filter((providerNamespace) => !BUILT_IN_NAMESPACES.has(providerNamespace));
44
45
  const extraEnvEntries = unique([
45
46
  ...authArtifacts.envEntries,
46
47
  ...authzArtifacts.envEntries,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { generateApiInfrastructureArtifacts } from '../../apiArtifacts';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { APP_NAMESPACE, generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;\n\nexport function generateMinikubeInfra(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (manifest.deployment?.target !== 'minikube') {\n throw new Error(\n `Minikube adapter only supports deployment target \"minikube\" (received \"${manifest.deployment?.target ?? 'undefined'}\").`,\n );\n }\n\n if (manifest.database?.provider && manifest.database.provider !== 'supabase') {\n throw new Error(\n `Unsupported database provider for minikube adapter: \"${manifest.database.provider}\". Only \"supabase\" is implemented.`,\n );\n }\n\n const appSlug = validateCanonicalAppSlug(options.appManifest?.metadata.slug);\n const namespace = APP_NAMESPACE;\n\n const authArtifacts = generateAuthProviderArtifacts({ manifest, namespace });\n const authzArtifacts = generateAuthorizationArtifacts({\n manifest,\n namespace,\n appManifest: options.appManifest,\n });\n const storageArtifacts = generateStorageArtifacts({ manifest, namespace });\n const secretStoreArtifacts = generateSecretStoreArtifacts({ manifest, namespace });\n const apiArtifacts = generateApiInfrastructureArtifacts({\n data: options.appManifest?.data,\n databaseProvider: manifest.database?.provider,\n });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\n const providerLifecycle = [\n ...authArtifacts.providerLifecycle,\n ...authzArtifacts.providerLifecycle,\n ...storageArtifacts.providerLifecycle,\n ...secretStoreArtifacts.providerLifecycle,\n ];\n const providerNamespaces = unique([\n ...providerLifecycle.map((contribution) => contribution.namespace),\n ]);\n const extraEnvEntries = unique([\n ...authArtifacts.envEntries,\n ...authzArtifacts.envEntries,\n ...storageArtifacts.envEntries,\n ...secretStoreArtifacts.envEntries,\n ]);\n\n const baseFiles = generateMinikubeBaseArtifacts({\n manifest,\n appSlug,\n extraResources,\n providerNamespaces,\n providerLifecycle,\n extraEnvEntries,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ...apiArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ...apiArtifacts.files,\n ],\n warnings,\n meta: {\n target: 'minikube',\n providers: collectProviders(manifest),\n },\n dependencies: collectDependencies(manifest),\n };\n}\n\nfunction collectProviders(manifest: InfraManifestInput): string[] {\n const providers = new Set<string>();\n\n if (manifest.auth?.provider) providers.add(manifest.auth.provider);\n if (manifest.auth?.authorization?.engine) {\n providers.add(manifest.auth.authorization.engine);\n }\n if (manifest.database?.provider) providers.add(manifest.database.provider);\n if (manifest.storage?.provider) providers.add(manifest.storage.provider);\n if (manifest.secretStore?.provider) providers.add(manifest.secretStore.provider);\n if (manifest.state?.provider) providers.add(manifest.state.provider);\n\n return [...providers].sort();\n}\n\nfunction collectDependencies(manifest: InfraManifestInput): readonly GeneratedPackageDependency[] {\n if (manifest.state?.provider !== 'legend') {\n return [];\n }\n\n return [\n {\n name: '@ankhorage/state-legend',\n version: '^0.1.0',\n reason: 'Selected by infra.state.provider=legend.',\n },\n ];\n}\n\nfunction collectWarnings(manifest: InfraManifestInput): string[] {\n const warnings: string[] = [];\n\n if (manifest.deployment?.monitoring) {\n warnings.push(\n 'Monitoring is enabled in manifest, but monitoring stack resources are not generated yet.',\n );\n }\n\n if (manifest.networking?.cdn) {\n warnings.push('CDN is enabled in manifest, but CDN resources are not generated for minikube.');\n }\n\n return warnings;\n}\n\nfunction validateCanonicalAppSlug(slug: string | undefined): string {\n if (!slug) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug is required.',\n );\n }\n\n if (slug.trim() !== slug || !CANONICAL_PROJECT_SLUG_RE.test(slug)) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters using only a-z, 0-9, and hyphens, without leading or trailing hyphens.',\n );\n }\n\n return slug;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/minikube/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { generateApiInfrastructureArtifacts } from '../../apiArtifacts';\nimport type {\n GeneratedPackageDependency,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from '../../types';\nimport { generateAuthProviderArtifacts } from './auth';\nimport { generateAuthorizationArtifacts } from './authz';\nimport { APP_NAMESPACE, generateMinikubeBaseArtifacts } from './base';\nimport { generateSecretStoreArtifacts } from './secrets';\nimport { generateStorageArtifacts } from './storage';\n\nconst CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;\nconst BUILT_IN_NAMESPACES = new Set([APP_NAMESPACE, 'supabase']);\n\nexport function generateMinikubeInfra(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (manifest.deployment?.target !== 'minikube') {\n throw new Error(\n `Minikube adapter only supports deployment target \"minikube\" (received \"${manifest.deployment?.target ?? 'undefined'}\").`,\n );\n }\n\n if (manifest.database?.provider && manifest.database.provider !== 'supabase') {\n throw new Error(\n `Unsupported database provider for minikube adapter: \"${manifest.database.provider}\". Only \"supabase\" is implemented.`,\n );\n }\n\n const appSlug = validateCanonicalAppSlug(options.appManifest?.metadata.slug);\n const namespace = APP_NAMESPACE;\n\n const authArtifacts = generateAuthProviderArtifacts({ manifest, namespace });\n const authzArtifacts = generateAuthorizationArtifacts({\n manifest,\n namespace,\n appManifest: options.appManifest,\n });\n const storageArtifacts = generateStorageArtifacts({ manifest, namespace });\n const secretStoreArtifacts = generateSecretStoreArtifacts({ manifest, namespace });\n const apiArtifacts = generateApiInfrastructureArtifacts({\n data: options.appManifest?.data,\n databaseProvider: manifest.database?.provider,\n });\n\n const extraResources = unique([\n ...authArtifacts.resources,\n ...authzArtifacts.resources,\n ...storageArtifacts.resources,\n ...secretStoreArtifacts.resources,\n ]);\n const providerLifecycle = [\n ...authArtifacts.providerLifecycle,\n ...authzArtifacts.providerLifecycle,\n ...storageArtifacts.providerLifecycle,\n ...secretStoreArtifacts.providerLifecycle,\n ];\n const providerNamespaces = unique([\n ...providerLifecycle.map((contribution) => contribution.namespace),\n ]).filter((providerNamespace) => !BUILT_IN_NAMESPACES.has(providerNamespace));\n const extraEnvEntries = unique([\n ...authArtifacts.envEntries,\n ...authzArtifacts.envEntries,\n ...storageArtifacts.envEntries,\n ...secretStoreArtifacts.envEntries,\n ]);\n\n const baseFiles = generateMinikubeBaseArtifacts({\n manifest,\n appSlug,\n extraResources,\n providerNamespaces,\n providerLifecycle,\n extraEnvEntries,\n });\n\n const warnings = unique([\n ...collectWarnings(manifest),\n ...authArtifacts.warnings,\n ...authzArtifacts.warnings,\n ...storageArtifacts.warnings,\n ...secretStoreArtifacts.warnings,\n ...apiArtifacts.warnings,\n ]);\n\n return {\n files: [\n ...baseFiles,\n ...authArtifacts.files,\n ...authzArtifacts.files,\n ...storageArtifacts.files,\n ...secretStoreArtifacts.files,\n ...apiArtifacts.files,\n ],\n warnings,\n meta: {\n target: 'minikube',\n providers: collectProviders(manifest),\n },\n dependencies: collectDependencies(manifest),\n };\n}\n\nfunction collectProviders(manifest: InfraManifestInput): string[] {\n const providers = new Set<string>();\n\n if (manifest.auth?.provider) providers.add(manifest.auth.provider);\n if (manifest.auth?.authorization?.engine) {\n providers.add(manifest.auth.authorization.engine);\n }\n if (manifest.database?.provider) providers.add(manifest.database.provider);\n if (manifest.storage?.provider) providers.add(manifest.storage.provider);\n if (manifest.secretStore?.provider) providers.add(manifest.secretStore.provider);\n if (manifest.state?.provider) providers.add(manifest.state.provider);\n\n return [...providers].sort();\n}\n\nfunction collectDependencies(manifest: InfraManifestInput): readonly GeneratedPackageDependency[] {\n if (manifest.state?.provider !== 'legend') {\n return [];\n }\n\n return [\n {\n name: '@ankhorage/state-legend',\n version: '^0.1.0',\n reason: 'Selected by infra.state.provider=legend.',\n },\n ];\n}\n\nfunction collectWarnings(manifest: InfraManifestInput): string[] {\n const warnings: string[] = [];\n\n if (manifest.deployment?.monitoring) {\n warnings.push(\n 'Monitoring is enabled in manifest, but monitoring stack resources are not generated yet.',\n );\n }\n\n if (manifest.networking?.cdn) {\n warnings.push('CDN is enabled in manifest, but CDN resources are not generated for minikube.');\n }\n\n return warnings;\n}\n\nfunction validateCanonicalAppSlug(slug: string | undefined): string {\n if (!slug) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug is required.',\n );\n }\n\n if (slug.trim() !== slug || !CANONICAL_PROJECT_SLUG_RE.test(slug)) {\n throw new Error(\n 'Cannot generate Minikube infrastructure: appManifest.metadata.slug must be a canonical lowercase slug up to 40 characters using only a-z, 0-9, and hyphens, without leading or trailing hyphens.',\n );\n }\n\n return slug;\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n"]}
@@ -0,0 +1,19 @@
1
+ export type AuthRedirectEnvironment = 'local' | 'preview' | 'production';
2
+ export interface ResolveAuthRedirectConfigurationInput {
3
+ environment: AuthRedirectEnvironment;
4
+ gatewayOrigin: string;
5
+ siteOrigin: string;
6
+ callbackRoute: string;
7
+ webOrigins?: readonly string[];
8
+ nativeRedirectUris?: readonly string[];
9
+ }
10
+ export interface AuthRedirectConfiguration {
11
+ providerCallbackUrl: string;
12
+ siteUrl: string;
13
+ redirectAllowList: readonly string[];
14
+ serializedRedirectAllowList: string;
15
+ }
16
+ export declare function resolveAuthRedirectConfiguration(input: ResolveAuthRedirectConfigurationInput): AuthRedirectConfiguration;
17
+ export declare function getLocalAuthRedirectPatterns(callbackRoute: string): readonly string[];
18
+ export declare function normalizeAuthCallbackRoute(callbackRoute: string): string;
19
+ //# sourceMappingURL=authRedirects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authRedirects.d.ts","sourceRoot":"","sources":["../src/authRedirects.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,qCAAqC;IACpD,WAAW,EAAE,uBAAuB,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,yBAAyB;IACxC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,2BAA2B,EAAE,MAAM,CAAC;CACrC;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,qCAAqC,GAC3C,yBAAyB,CAyB3B;AAED,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAGrF;AAED,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAExE"}
@@ -0,0 +1,80 @@
1
+ export function resolveAuthRedirectConfiguration(input) {
2
+ const gatewayOrigin = normalizeWebOrigin(input.gatewayOrigin, 'gatewayOrigin');
3
+ const siteUrl = normalizeWebOrigin(input.siteOrigin, 'siteOrigin');
4
+ const callbackRoute = normalizeCallbackRoute(input.callbackRoute);
5
+ const webOrigins = unique([
6
+ siteUrl,
7
+ ...(input.webOrigins ?? []).map((origin) => normalizeWebOrigin(origin, 'webOrigins')),
8
+ ]);
9
+ const nativeRedirectUris = unique((input.nativeRedirectUris ?? []).map((uri) => normalizeNativeRedirectUri(uri)));
10
+ const redirectAllowList = unique([
11
+ ...webOrigins,
12
+ ...webOrigins.map((origin) => `${origin}${callbackRoute}`),
13
+ ...(input.environment === 'local' ? getLocalAuthRedirectPatterns(callbackRoute) : []),
14
+ ...nativeRedirectUris,
15
+ ]);
16
+ return {
17
+ providerCallbackUrl: `${gatewayOrigin}/auth/v1/callback`,
18
+ siteUrl,
19
+ redirectAllowList,
20
+ serializedRedirectAllowList: redirectAllowList.join(','),
21
+ };
22
+ }
23
+ export function getLocalAuthRedirectPatterns(callbackRoute) {
24
+ const normalizedRoute = normalizeCallbackRoute(callbackRoute);
25
+ return [`http://127.0.0.1:*${normalizedRoute}`, `http://localhost:*${normalizedRoute}`];
26
+ }
27
+ export function normalizeAuthCallbackRoute(callbackRoute) {
28
+ return normalizeCallbackRoute(callbackRoute);
29
+ }
30
+ function normalizeCallbackRoute(callbackRoute) {
31
+ const trimmed = callbackRoute.trim();
32
+ if (!trimmed) {
33
+ throw new Error('Auth callback route must not be empty.');
34
+ }
35
+ const withLeadingSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
36
+ const normalized = withLeadingSlash.replace(/\/{2,}/gu, '/');
37
+ if (normalized.includes('?') || normalized.includes('#')) {
38
+ throw new Error('Auth callback route must not include a query string or fragment.');
39
+ }
40
+ return normalized;
41
+ }
42
+ function normalizeWebOrigin(value, field) {
43
+ const parsed = parseUrl(value, field);
44
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
45
+ throw new Error(`${field} must use http or https.`);
46
+ }
47
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
48
+ throw new Error(`${field} must be a canonical origin without credentials, query, or fragment.`);
49
+ }
50
+ if (parsed.pathname !== '/' && parsed.pathname !== '') {
51
+ throw new Error(`${field} must not include a path.`);
52
+ }
53
+ return parsed.origin;
54
+ }
55
+ function normalizeNativeRedirectUri(value) {
56
+ const parsed = parseUrl(value, 'nativeRedirectUris');
57
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
58
+ throw new Error('nativeRedirectUris must use a non-HTTP application scheme.');
59
+ }
60
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
61
+ throw new Error('nativeRedirectUris must not include credentials, query strings, or fragments.');
62
+ }
63
+ return parsed.toString();
64
+ }
65
+ function parseUrl(value, field) {
66
+ const trimmed = value.trim();
67
+ if (!trimmed) {
68
+ throw new Error(`${field} must not contain empty values.`);
69
+ }
70
+ try {
71
+ return new URL(trimmed);
72
+ }
73
+ catch {
74
+ throw new Error(`${field} contains an invalid URL.`);
75
+ }
76
+ }
77
+ function unique(values) {
78
+ return [...new Set(values)];
79
+ }
80
+ //# sourceMappingURL=authRedirects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authRedirects.js","sourceRoot":"","sources":["../src/authRedirects.ts"],"names":[],"mappings":"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","sourcesContent":["export type AuthRedirectEnvironment = 'local' | 'preview' | 'production';\n\nexport interface ResolveAuthRedirectConfigurationInput {\n environment: AuthRedirectEnvironment;\n gatewayOrigin: string;\n siteOrigin: string;\n callbackRoute: string;\n webOrigins?: readonly string[];\n nativeRedirectUris?: readonly string[];\n}\n\nexport interface AuthRedirectConfiguration {\n providerCallbackUrl: string;\n siteUrl: string;\n redirectAllowList: readonly string[];\n serializedRedirectAllowList: string;\n}\n\nexport function resolveAuthRedirectConfiguration(\n input: ResolveAuthRedirectConfigurationInput,\n): AuthRedirectConfiguration {\n const gatewayOrigin = normalizeWebOrigin(input.gatewayOrigin, 'gatewayOrigin');\n const siteUrl = normalizeWebOrigin(input.siteOrigin, 'siteOrigin');\n const callbackRoute = normalizeCallbackRoute(input.callbackRoute);\n const webOrigins = unique([\n siteUrl,\n ...(input.webOrigins ?? []).map((origin) => normalizeWebOrigin(origin, 'webOrigins')),\n ]);\n const nativeRedirectUris = unique(\n (input.nativeRedirectUris ?? []).map((uri) => normalizeNativeRedirectUri(uri)),\n );\n\n const redirectAllowList = unique([\n ...webOrigins,\n ...webOrigins.map((origin) => `${origin}${callbackRoute}`),\n ...(input.environment === 'local' ? getLocalAuthRedirectPatterns(callbackRoute) : []),\n ...nativeRedirectUris,\n ]);\n\n return {\n providerCallbackUrl: `${gatewayOrigin}/auth/v1/callback`,\n siteUrl,\n redirectAllowList,\n serializedRedirectAllowList: redirectAllowList.join(','),\n };\n}\n\nexport function getLocalAuthRedirectPatterns(callbackRoute: string): readonly string[] {\n const normalizedRoute = normalizeCallbackRoute(callbackRoute);\n return [`http://127.0.0.1:*${normalizedRoute}`, `http://localhost:*${normalizedRoute}`];\n}\n\nexport function normalizeAuthCallbackRoute(callbackRoute: string): string {\n return normalizeCallbackRoute(callbackRoute);\n}\n\nfunction normalizeCallbackRoute(callbackRoute: string): string {\n const trimmed = callbackRoute.trim();\n if (!trimmed) {\n throw new Error('Auth callback route must not be empty.');\n }\n\n const withLeadingSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n const normalized = withLeadingSlash.replace(/\\/{2,}/gu, '/');\n if (normalized.includes('?') || normalized.includes('#')) {\n throw new Error('Auth callback route must not include a query string or fragment.');\n }\n return normalized;\n}\n\nfunction normalizeWebOrigin(value: string, field: string): string {\n const parsed = parseUrl(value, field);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error(`${field} must use http or https.`);\n }\n if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new Error(`${field} must be a canonical origin without credentials, query, or fragment.`);\n }\n if (parsed.pathname !== '/' && parsed.pathname !== '') {\n throw new Error(`${field} must not include a path.`);\n }\n return parsed.origin;\n}\n\nfunction normalizeNativeRedirectUri(value: string): string {\n const parsed = parseUrl(value, 'nativeRedirectUris');\n if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {\n throw new Error('nativeRedirectUris must use a non-HTTP application scheme.');\n }\n if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new Error(\n 'nativeRedirectUris must not include credentials, query strings, or fragments.',\n );\n }\n return parsed.toString();\n}\n\nfunction parseUrl(value: string, field: string): URL {\n const trimmed = value.trim();\n if (!trimmed) {\n throw new Error(`${field} must not contain empty values.`);\n }\n\n try {\n return new URL(trimmed);\n } catch {\n throw new Error(`${field} contains an invalid URL.`);\n }\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { InfraGenerationInput, InfraManifestInput, InfrastructureGenerationOptions, InfrastructureGenerationResult } from './types';
2
2
  export type { ApiInfrastructureArtifacts } from './apiArtifacts';
3
3
  export { generateApiInfrastructureArtifacts } from './apiArtifacts';
4
+ export { type AuthRedirectConfiguration, type AuthRedirectEnvironment, getLocalAuthRedirectPatterns, normalizeAuthCallbackRoute, resolveAuthRedirectConfiguration, type ResolveAuthRedirectConfigurationInput, } from './authRedirects';
4
5
  export { createInfraSecretStoreAdapter, type CreateInfraSecretStoreAdapterInput, type InfraSecretStoreProviders, } from './secretStore';
5
6
  export type { GeneratedFile, GeneratedInfrastructureFile, GeneratedPackageDependency, InfraDiagnostic, InfraGenerationInput, InfraGenerationOptions, InfraGenerationResult, InfraManifestInput, InfrastructureGenerationMeta, InfrastructureGenerationOptions, InfrastructureGenerationResult, PackageDependency, } from './types';
6
7
  export declare function generateInfra(input: InfraGenerationInput): InfrastructureGenerationResult;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,6BAA6B,EAC7B,KAAK,kCAAkC,EACvC,KAAK,yBAAyB,GAC/B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,aAAa,EACb,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,4BAA4B,EAC5B,+BAA+B,EAC/B,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,KAAK,EAAE,oBAAoB,GAAG,8BAA8B,CAEzF;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAUhC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,0BAA0B,EAC1B,gCAAgC,EAChC,KAAK,qCAAqC,GAC3C,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,6BAA6B,EAC7B,KAAK,kCAAkC,EACvC,KAAK,yBAAyB,GAC/B,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,aAAa,EACb,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,4BAA4B,EAC5B,+BAA+B,EAC/B,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,KAAK,EAAE,oBAAoB,GAAG,8BAA8B,CAEzF;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,+BAAoC,GAC5C,8BAA8B,CAUhC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { generateMinikubeInfra } from './adapters/minikube';
2
2
  export { generateApiInfrastructureArtifacts } from './apiArtifacts';
3
+ export { getLocalAuthRedirectPatterns, normalizeAuthCallbackRoute, resolveAuthRedirectConfiguration, } from './authRedirects';
3
4
  export { createInfraSecretStoreAdapter, } from './secretStore';
4
5
  export function generateInfra(input) {
5
6
  return generateInfrastructure(input.manifest, input.options ?? {});
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAS5D,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,6BAA6B,GAG9B,MAAM,eAAe,CAAC;AAgBvB,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,OAAO,sBAAsB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAA4B,EAC5B,UAA2C,EAAE;IAE7C,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { generateMinikubeInfra } from './adapters/minikube';\nimport type {\n InfraGenerationInput,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from './types';\n\nexport type { ApiInfrastructureArtifacts } from './apiArtifacts';\nexport { generateApiInfrastructureArtifacts } from './apiArtifacts';\nexport {\n createInfraSecretStoreAdapter,\n type CreateInfraSecretStoreAdapterInput,\n type InfraSecretStoreProviders,\n} from './secretStore';\nexport type {\n GeneratedFile,\n GeneratedInfrastructureFile,\n GeneratedPackageDependency,\n InfraDiagnostic,\n InfraGenerationInput,\n InfraGenerationOptions,\n InfraGenerationResult,\n InfraManifestInput,\n InfrastructureGenerationMeta,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n PackageDependency,\n} from './types';\n\nexport function generateInfra(input: InfraGenerationInput): InfrastructureGenerationResult {\n return generateInfrastructure(input.manifest, input.options ?? {});\n}\n\nexport function generateInfrastructure(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (!manifest.deployment) {\n throw new Error('Deployment configuration is missing.');\n }\n\n if (manifest.deployment.target !== 'minikube') {\n throw new Error(`Unsupported deployment target: ${manifest.deployment.target}`);\n }\n\n return generateMinikubeInfra(manifest, options);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAS5D,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAGL,4BAA4B,EAC5B,0BAA0B,EAC1B,gCAAgC,GAEjC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,6BAA6B,GAG9B,MAAM,eAAe,CAAC;AAgBvB,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,OAAO,sBAAsB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAA4B,EAC5B,UAA2C,EAAE;IAE7C,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { generateMinikubeInfra } from './adapters/minikube';\nimport type {\n InfraGenerationInput,\n InfraManifestInput,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n} from './types';\n\nexport type { ApiInfrastructureArtifacts } from './apiArtifacts';\nexport { generateApiInfrastructureArtifacts } from './apiArtifacts';\nexport {\n type AuthRedirectConfiguration,\n type AuthRedirectEnvironment,\n getLocalAuthRedirectPatterns,\n normalizeAuthCallbackRoute,\n resolveAuthRedirectConfiguration,\n type ResolveAuthRedirectConfigurationInput,\n} from './authRedirects';\nexport {\n createInfraSecretStoreAdapter,\n type CreateInfraSecretStoreAdapterInput,\n type InfraSecretStoreProviders,\n} from './secretStore';\nexport type {\n GeneratedFile,\n GeneratedInfrastructureFile,\n GeneratedPackageDependency,\n InfraDiagnostic,\n InfraGenerationInput,\n InfraGenerationOptions,\n InfraGenerationResult,\n InfraManifestInput,\n InfrastructureGenerationMeta,\n InfrastructureGenerationOptions,\n InfrastructureGenerationResult,\n PackageDependency,\n} from './types';\n\nexport function generateInfra(input: InfraGenerationInput): InfrastructureGenerationResult {\n return generateInfrastructure(input.manifest, input.options ?? {});\n}\n\nexport function generateInfrastructure(\n manifest: InfraManifestInput,\n options: InfrastructureGenerationOptions = {},\n): InfrastructureGenerationResult {\n if (!manifest.deployment) {\n throw new Error('Deployment configuration is missing.');\n }\n\n if (manifest.deployment.target !== 'minikube') {\n throw new Error(`Unsupported deployment target: ${manifest.deployment.target}`);\n }\n\n return generateMinikubeInfra(manifest, options);\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/infra",
3
3
  "type": "module",
4
- "version": "1.0.5",
4
+ "version": "1.1.0",
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": {
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@ankhorage/contracts": "^2.1.0",
39
- "@ankhorage/supabase-vault": "^0.2.3"
39
+ "@ankhorage/supabase-vault": "^0.2.4"
40
40
  },
41
41
  "files": [
42
42
  "dist",
@@ -1,5 +1,6 @@
1
1
  import type { InfraManifestInput } from '../../../types';
2
2
  import { emptyMinikubeArtifacts, type MinikubeAdapterArtifacts } from '../contracts';
3
+ import { generateSupabaseOAuthRuntimeArtifacts } from './oauthRuntime';
3
4
  import { generateSupabaseAuthArtifacts } from './supabase';
4
5
 
5
6
  export function generateAuthProviderArtifacts(args: {
@@ -19,5 +20,17 @@ export function generateAuthProviderArtifacts(args: {
19
20
  );
20
21
  }
21
22
 
22
- return generateSupabaseAuthArtifacts({ manifest, namespace });
23
+ const providerArtifacts = generateSupabaseAuthArtifacts({ manifest, namespace });
24
+ const oauthArtifacts = generateSupabaseOAuthRuntimeArtifacts(manifest);
25
+
26
+ return {
27
+ files: [...providerArtifacts.files, ...oauthArtifacts.files],
28
+ resources: [...providerArtifacts.resources, ...oauthArtifacts.resources],
29
+ providerLifecycle: [
30
+ ...providerArtifacts.providerLifecycle,
31
+ ...oauthArtifacts.providerLifecycle,
32
+ ],
33
+ envEntries: [...providerArtifacts.envEntries, ...oauthArtifacts.envEntries],
34
+ warnings: [...providerArtifacts.warnings, ...oauthArtifacts.warnings],
35
+ };
23
36
  }
@@ -0,0 +1,169 @@
1
+ import { getLocalAuthRedirectPatterns, normalizeAuthCallbackRoute } from '../../../authRedirects';
2
+ import type { InfraManifestInput } from '../../../types';
3
+ import type { MinikubeAdapterArtifacts, MinikubeProviderLifecycle } from '../contracts';
4
+
5
+ interface SupabaseOAuthRuntimeModel {
6
+ callbackRoute: string;
7
+ envPrefixes: readonly string[];
8
+ localRedirectPatterns: readonly string[];
9
+ }
10
+
11
+ export function generateSupabaseOAuthRuntimeArtifacts(
12
+ manifest: InfraManifestInput,
13
+ ): MinikubeAdapterArtifacts {
14
+ const oauthRuntime = resolveOAuthRuntimeModel(manifest);
15
+ if (!oauthRuntime) {
16
+ return {
17
+ files: [],
18
+ resources: [],
19
+ providerLifecycle: [],
20
+ envEntries: [],
21
+ warnings: [],
22
+ };
23
+ }
24
+
25
+ return {
26
+ files: [
27
+ {
28
+ path: 'infra/minikube/auth/oauth-runtime.md',
29
+ content: getOAuthRuntimeGuide(oauthRuntime),
30
+ },
31
+ ],
32
+ resources: [],
33
+ providerLifecycle: [getOAuthProviderLifecycle(oauthRuntime)],
34
+ envEntries: [],
35
+ warnings: [],
36
+ };
37
+ }
38
+
39
+ function resolveOAuthRuntimeModel(manifest: InfraManifestInput): SupabaseOAuthRuntimeModel | null {
40
+ const oauth = manifest.auth?.oauth;
41
+ if (!oauth?.enabled) return null;
42
+
43
+ const providers = oauth.providers.filter((provider) => provider.enabled !== false);
44
+ if (providers.length === 0) return null;
45
+
46
+ const callbackRoute = normalizeAuthCallbackRoute(oauth.callbackRoute);
47
+ const envPrefixes = providers.map((provider) => {
48
+ const prefix = provider.id
49
+ .trim()
50
+ .toUpperCase()
51
+ .replace(/[^A-Z0-9]+/gu, '_')
52
+ .replace(/^_+|_+$/gu, '');
53
+ if (!prefix) {
54
+ throw new Error(
55
+ `OAuth provider "${provider.id}" cannot be mapped to GoTrue environment keys.`,
56
+ );
57
+ }
58
+ return prefix;
59
+ });
60
+
61
+ return {
62
+ callbackRoute,
63
+ envPrefixes,
64
+ localRedirectPatterns: getLocalAuthRedirectPatterns(callbackRoute),
65
+ };
66
+ }
67
+
68
+ function getOAuthProviderLifecycle(
69
+ oauthRuntime: SupabaseOAuthRuntimeModel,
70
+ ): MinikubeProviderLifecycle {
71
+ return {
72
+ id: 'supabase-auth',
73
+ namespace: 'supabase',
74
+ endpoints: [],
75
+ readinessChecks: [
76
+ {
77
+ label: 'GoTrue',
78
+ namespace: 'supabase',
79
+ resource: 'deployment/auth',
80
+ timeoutSeconds: 600,
81
+ },
82
+ ],
83
+ migrationCommands: [],
84
+ reconciliationCommands: [
85
+ {
86
+ label: 'OAuth redirect and runtime rollout reconciliation',
87
+ command: getOAuthRuntimeReconciliationCommand(oauthRuntime),
88
+ },
89
+ ],
90
+ statusChecks: [
91
+ {
92
+ label: 'OAuth redirect configuration',
93
+ command: getOAuthRuntimeStatusCommand(oauthRuntime),
94
+ },
95
+ ],
96
+ };
97
+ }
98
+
99
+ function getOAuthRuntimeReconciliationCommand(oauthRuntime: SupabaseOAuthRuntimeModel): string {
100
+ const localPatterns = oauthRuntime.localRedirectPatterns.join(',');
101
+ const providerRedirectAssignments = oauthRuntime.envPrefixes
102
+ .map((prefix) => `GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI="\${oauth_provider_callback}"`)
103
+ .join(' ');
104
+ const providerExports = oauthRuntime.envPrefixes
105
+ .map(
106
+ (prefix) => `export GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI="\${oauth_provider_callback}"
107
+ write_env_value GOTRUE_EXTERNAL_${prefix}_REDIRECT_URI "\${oauth_provider_callback}"`,
108
+ )
109
+ .join('\n');
110
+
111
+ return `oauth_callback_route='${oauthRuntime.callbackRoute}'
112
+ oauth_callback_path="\${oauth_callback_route#/}"
113
+ oauth_site_url="\${SITE_URL%/}"
114
+ oauth_provider_callback="\${API_EXTERNAL_URL%/}/callback"
115
+ oauth_redirect_allow_list="\${oauth_site_url},\${oauth_site_url}/\${oauth_callback_path},${localPatterns}"
116
+ if [[ -n "\${OAUTH_NATIVE_REDIRECT_URLS:-}" ]]; then
117
+ oauth_redirect_allow_list="\${oauth_redirect_allow_list},\${OAUTH_NATIVE_REDIRECT_URLS}"
118
+ fi
119
+ export ADDITIONAL_REDIRECT_URLS="\${oauth_redirect_allow_list}"
120
+ write_env_value ADDITIONAL_REDIRECT_URLS "\${oauth_redirect_allow_list}"
121
+ ${providerExports}
122
+ kubectl --context "\${PROFILE}" -n supabase set env deployment/auth \\
123
+ API_EXTERNAL_URL="\${API_EXTERNAL_URL}" \\
124
+ GOTRUE_SITE_URL="\${SITE_URL}" \\
125
+ GOTRUE_URI_ALLOW_LIST="\${oauth_redirect_allow_list}" \\
126
+ GOTRUE_JWT_ISSUER="\${API_EXTERNAL_URL}" \\
127
+ ${providerRedirectAssignments} >/dev/null
128
+ kubectl --context "\${PROFILE}" -n supabase rollout restart deployment/auth >/dev/null
129
+ kubectl --context "\${PROFILE}" -n supabase rollout status deployment/auth --timeout=600s`;
130
+ }
131
+
132
+ function getOAuthRuntimeStatusCommand(oauthRuntime: SupabaseOAuthRuntimeModel): string {
133
+ return `oauth_callback_route='${oauthRuntime.callbackRoute}'
134
+ oauth_callback_path="\${oauth_callback_route#/}"
135
+ if [[ -n "\${API_EXTERNAL_URL:-}" ]]; then
136
+ echo "- provider supabase-auth/provider-callback: \${API_EXTERNAL_URL%/}/callback"
137
+ else
138
+ echo "- provider supabase-auth/provider-callback: unavailable"
139
+ fi
140
+ if [[ -n "\${SITE_URL:-}" ]]; then
141
+ echo "- provider supabase-auth/app-callback: \${SITE_URL%/}/\${oauth_callback_path}"
142
+ else
143
+ echo "- provider supabase-auth/app-callback: unavailable"
144
+ fi
145
+ echo "- provider supabase-auth/local-callback-patterns: ${oauthRuntime.localRedirectPatterns.join(',')}"`;
146
+ }
147
+
148
+ function getOAuthRuntimeGuide(oauthRuntime: SupabaseOAuthRuntimeModel): string {
149
+ const localPatterns = oauthRuntime.localRedirectPatterns
150
+ .map((pattern) => `- \`${pattern}\``)
151
+ .join('\n');
152
+
153
+ return `# Supabase OAuth Runtime Reconciliation
154
+
155
+ The provider callback is derived from the active project gateway as
156
+ \`\${API_EXTERNAL_URL%/}/callback\`, which resolves to the project-owned
157
+ \`/auth/v1/callback\` endpoint. GoTrue-to-app redirects use the configured callback route
158
+ \`${oauthRuntime.callbackRoute}\`.
159
+
160
+ Local Minikube reconciliation adds only callback-scoped loopback patterns:
161
+
162
+ ${localPatterns}
163
+
164
+ Native callback URIs may be supplied through \`OAUTH_NATIVE_REDIRECT_URLS\`. The generated
165
+ provider lifecycle writes only redirect metadata, never OAuth client secrets. It updates the
166
+ GoTrue deployment environment, forces \`deployment/auth\` to restart, and waits up to 600
167
+ seconds for the rollout. A failed rollout stops Infra Up before its success message.
168
+ `;
169
+ }
@@ -0,0 +1,143 @@
1
+ import type { InfraManifest } from '@ankhorage/contracts';
2
+ import { describe, expect, test } from 'bun:test';
3
+
4
+ import { generateInfrastructure } from '../../../../index';
5
+ import { createAppManifest } from '../../../../testSupport';
6
+ import { generateSupabaseOAuthRuntimeArtifacts } from '../oauthRuntime';
7
+
8
+ describe('Supabase OAuth runtime reconciliation', () => {
9
+ test('contributes callback-scoped local redirects and a bounded GoTrue restart', () => {
10
+ const artifacts = generateSupabaseOAuthRuntimeArtifacts(createOAuthManifest());
11
+ const [lifecycle] = artifacts.providerLifecycle;
12
+ const command = lifecycle?.reconciliationCommands[0]?.command ?? '';
13
+ const statusCommand = lifecycle?.statusChecks[0]?.command ?? '';
14
+ const guide = getFile(artifacts.files, 'infra/minikube/auth/oauth-runtime.md');
15
+
16
+ expect(lifecycle?.id).toBe('supabase-auth');
17
+ expect(lifecycle?.namespace).toBe('supabase');
18
+ expect(lifecycle?.readinessChecks).toEqual([
19
+ {
20
+ label: 'GoTrue',
21
+ namespace: 'supabase',
22
+ resource: 'deployment/auth',
23
+ timeoutSeconds: 600,
24
+ },
25
+ ]);
26
+ expect(command).toContain('oauth_provider_callback="${API_EXTERNAL_URL%/}/callback"');
27
+ expect(command).toContain('http://127.0.0.1:*/auth/callback');
28
+ expect(command).toContain('http://localhost:*/auth/callback');
29
+ expect(command).toContain('OAUTH_NATIVE_REDIRECT_URLS');
30
+ expect(command).toContain('GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI="${oauth_provider_callback}"');
31
+ expect(command).toContain('GOTRUE_URI_ALLOW_LIST="${oauth_redirect_allow_list}"');
32
+ expect(command).toContain('rollout restart deployment/auth');
33
+ expect(command).toContain('rollout status deployment/auth --timeout=600s');
34
+ expect(command.indexOf('set env deployment/auth')).toBeLessThan(
35
+ command.indexOf('rollout restart deployment/auth'),
36
+ );
37
+ expect(command.indexOf('rollout restart deployment/auth')).toBeLessThan(
38
+ command.indexOf('rollout status deployment/auth'),
39
+ );
40
+ expect(command).not.toContain('clientSecret');
41
+ expect(command).not.toContain('GOTRUE_EXTERNAL_GOOGLE_SECRET=');
42
+ expect(statusCommand).toContain('provider supabase-auth/provider-callback');
43
+ expect(statusCommand).toContain('provider supabase-auth/app-callback');
44
+ expect(guide).toContain('Supabase OAuth Runtime Reconciliation');
45
+ expect(guide).toContain('A failed rollout stops Infra Up before its success message.');
46
+ });
47
+
48
+ test('integrates reconciliation after generated runtime resources without duplicate namespaces', () => {
49
+ const result = generateInfrastructure(createOAuthManifest(), {
50
+ appManifest: createAppManifest('oauth-app'),
51
+ });
52
+ const upScript = getFile(result.files, 'infra/minikube/scripts/up.sh');
53
+ const kustomization = getFile(result.files, 'infra/minikube/k8s/kustomization.yaml');
54
+ const statusScript = getFile(result.files, 'infra/minikube/scripts/status.sh');
55
+
56
+ expect(kustomization.match(/namespaces\/supabase\.yaml/gu)).toHaveLength(1);
57
+ expect(upScript).toContain(
58
+ 'Running provider supabase-auth OAuth redirect and runtime rollout reconciliation.',
59
+ );
60
+ expect(upScript).toContain(
61
+ 'kubectl --context "${PROFILE}" -n supabase set env deployment/auth',
62
+ );
63
+ expect(upScript).toContain(
64
+ 'kubectl --context "${PROFILE}" -n supabase rollout restart deployment/auth',
65
+ );
66
+ expect(upScript).toContain(
67
+ 'kubectl --context "${PROFILE}" -n supabase rollout status deployment/auth --timeout=600s',
68
+ );
69
+ expect(upScript.lastIndexOf('run_provider_reconciliation')).toBeLessThan(
70
+ upScript.lastIndexOf('echo "Minikube infrastructure for \'${PROFILE}\' is running."'),
71
+ );
72
+ expect(upScript.indexOf('credentialsRef auth/oauth/google')).toBeLessThan(
73
+ upScript.lastIndexOf('run_provider_reconciliation'),
74
+ );
75
+ expect(statusScript).toContain('provider supabase-auth/local-callback-patterns');
76
+ });
77
+
78
+ test('keeps canonical port groups distinct for concurrent projects', () => {
79
+ const first = generateInfrastructure(createOAuthManifest(), {
80
+ appManifest: createAppManifest('oauth-one'),
81
+ });
82
+ const second = generateInfrastructure(createOAuthManifest(), {
83
+ appManifest: createAppManifest('oauth-two'),
84
+ });
85
+
86
+ const firstEnv = getFile(first.files, 'infra/minikube/.env.example');
87
+ const secondEnv = getFile(second.files, 'infra/minikube/.env.example');
88
+ const firstGatewayPort = readEnvValue(firstEnv, 'SUPABASE_GATEWAY_FORWARD_LOCAL_PORT');
89
+ const secondGatewayPort = readEnvValue(secondEnv, 'SUPABASE_GATEWAY_FORWARD_LOCAL_PORT');
90
+ const firstAppPort = readEnvValue(firstEnv, 'APP_PORT_FORWARD_LOCAL_PORT');
91
+ const secondAppPort = readEnvValue(secondEnv, 'APP_PORT_FORWARD_LOCAL_PORT');
92
+
93
+ expect(firstGatewayPort).not.toBe(secondGatewayPort);
94
+ expect(firstAppPort).not.toBe(secondAppPort);
95
+ expect(`http://127.0.0.1:${firstGatewayPort}/auth/v1/callback`).not.toBe(
96
+ `http://127.0.0.1:${secondGatewayPort}/auth/v1/callback`,
97
+ );
98
+ });
99
+ });
100
+
101
+ function createOAuthManifest(): InfraManifest {
102
+ return {
103
+ deployment: {
104
+ target: 'minikube',
105
+ monitoring: false,
106
+ },
107
+ auth: {
108
+ scope: 'global',
109
+ provider: 'supabase',
110
+ oauth: {
111
+ enabled: true,
112
+ callbackRoute: '/auth/callback',
113
+ providers: [
114
+ {
115
+ id: 'google',
116
+ enabled: true,
117
+ credentialsRef: 'auth/oauth/google',
118
+ },
119
+ ],
120
+ },
121
+ },
122
+ database: {
123
+ provider: 'supabase',
124
+ tier: 'dev',
125
+ },
126
+ secretStore: {
127
+ provider: 'supabase-vault',
128
+ },
129
+ plugins: [],
130
+ };
131
+ }
132
+
133
+ function getFile(files: readonly { path: string; content: string }[], path: string): string {
134
+ const file = files.find((candidate) => candidate.path === path);
135
+ if (!file) throw new Error(`Missing generated file: ${path}`);
136
+ return file.content;
137
+ }
138
+
139
+ function readEnvValue(content: string, key: string): string {
140
+ const line = content.split('\n').find((candidate) => candidate.startsWith(`${key}=`));
141
+ if (!line) throw new Error(`Missing generated env key: ${key}`);
142
+ return line.slice(key.length + 1);
143
+ }
@@ -0,0 +1,36 @@
1
+ import type { InfraManifest } from '@ankhorage/contracts';
2
+ import { expect, test } from 'bun:test';
3
+
4
+ import { generateInfrastructure } from '../../../../index';
5
+ import { createAppManifest } from '../../../../testSupport';
6
+
7
+ test('keeps multiple provider redirect assignments as separate shell arguments', () => {
8
+ const manifest: InfraManifest = {
9
+ deployment: { target: 'minikube', monitoring: false },
10
+ auth: {
11
+ scope: 'global',
12
+ provider: 'supabase',
13
+ oauth: {
14
+ enabled: true,
15
+ callbackRoute: '/auth/callback',
16
+ providers: [
17
+ { id: 'google', enabled: true, credentialsRef: 'auth/oauth/google' },
18
+ { id: 'apple', enabled: true, credentialsRef: 'auth/oauth/apple' },
19
+ ],
20
+ },
21
+ },
22
+ database: { provider: 'supabase', tier: 'dev' },
23
+ secretStore: { provider: 'supabase-vault' },
24
+ plugins: [],
25
+ };
26
+ const result = generateInfrastructure(manifest, {
27
+ appManifest: createAppManifest('multi-oauth'),
28
+ });
29
+ const upScript = result.files.find(
30
+ (file) => file.path === 'infra/minikube/scripts/up.sh',
31
+ )?.content;
32
+
33
+ expect(upScript).toContain('GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI="${oauth_provider_callback}"');
34
+ expect(upScript).toContain('GOTRUE_EXTERNAL_APPLE_REDIRECT_URI="${oauth_provider_callback}"');
35
+ expect(upScript).not.toContain('\\ GOTRUE_EXTERNAL_APPLE_REDIRECT_URI');
36
+ });
@@ -12,6 +12,7 @@ import { generateSecretStoreArtifacts } from './secrets';
12
12
  import { generateStorageArtifacts } from './storage';
13
13
 
14
14
  const CANONICAL_PROJECT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
15
+ const BUILT_IN_NAMESPACES = new Set([APP_NAMESPACE, 'supabase']);
15
16
 
16
17
  export function generateMinikubeInfra(
17
18
  manifest: InfraManifestInput,
@@ -59,7 +60,7 @@ export function generateMinikubeInfra(
59
60
  ];
60
61
  const providerNamespaces = unique([
61
62
  ...providerLifecycle.map((contribution) => contribution.namespace),
62
- ]);
63
+ ]).filter((providerNamespace) => !BUILT_IN_NAMESPACES.has(providerNamespace));
63
64
  const extraEnvEntries = unique([
64
65
  ...authArtifacts.envEntries,
65
66
  ...authzArtifacts.envEntries,
@@ -0,0 +1,63 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { getLocalAuthRedirectPatterns, resolveAuthRedirectConfiguration } from './authRedirects';
4
+
5
+ describe('resolveAuthRedirectConfiguration', () => {
6
+ test('derives an exact provider callback and local browser callback policy', () => {
7
+ const config = resolveAuthRedirectConfiguration({
8
+ environment: 'local',
9
+ gatewayOrigin: 'http://127.0.0.1:18081/',
10
+ siteOrigin: 'http://127.0.0.1:18080/',
11
+ callbackRoute: 'auth/callback',
12
+ webOrigins: ['http://localhost:8081'],
13
+ nativeRedirectUris: ['ankh-demo://auth/callback'],
14
+ });
15
+
16
+ expect(config.providerCallbackUrl).toBe('http://127.0.0.1:18081/auth/v1/callback');
17
+ expect(config.siteUrl).toBe('http://127.0.0.1:18080');
18
+ expect(config.redirectAllowList).toEqual([
19
+ 'http://127.0.0.1:18080',
20
+ 'http://localhost:8081',
21
+ 'http://127.0.0.1:18080/auth/callback',
22
+ 'http://localhost:8081/auth/callback',
23
+ 'http://127.0.0.1:*/auth/callback',
24
+ 'http://localhost:*/auth/callback',
25
+ 'ankh-demo://auth/callback',
26
+ ]);
27
+ });
28
+
29
+ test('never leaks local wildcard policy into preview or production', () => {
30
+ for (const environment of ['preview', 'production'] as const) {
31
+ const config = resolveAuthRedirectConfiguration({
32
+ environment,
33
+ gatewayOrigin: 'https://api.example.test',
34
+ siteOrigin: 'https://app.example.test',
35
+ callbackRoute: '/auth/callback',
36
+ });
37
+
38
+ expect(config.redirectAllowList).toEqual([
39
+ 'https://app.example.test',
40
+ 'https://app.example.test/auth/callback',
41
+ ]);
42
+ expect(config.serializedRedirectAllowList).not.toContain('localhost');
43
+ expect(config.serializedRedirectAllowList).not.toContain('127.0.0.1');
44
+ expect(config.serializedRedirectAllowList).not.toContain('*');
45
+ }
46
+ });
47
+
48
+ test('normalizes callback routes and rejects unsafe origins', () => {
49
+ expect(getLocalAuthRedirectPatterns('//auth//callback')).toEqual([
50
+ 'http://127.0.0.1:*/auth/callback',
51
+ 'http://localhost:*/auth/callback',
52
+ ]);
53
+
54
+ expect(() =>
55
+ resolveAuthRedirectConfiguration({
56
+ environment: 'production',
57
+ gatewayOrigin: 'https://user:secret@example.test',
58
+ siteOrigin: 'https://app.example.test',
59
+ callbackRoute: '/auth/callback',
60
+ }),
61
+ ).toThrow('gatewayOrigin must be a canonical origin');
62
+ });
63
+ });
@@ -0,0 +1,113 @@
1
+ export type AuthRedirectEnvironment = 'local' | 'preview' | 'production';
2
+
3
+ export interface ResolveAuthRedirectConfigurationInput {
4
+ environment: AuthRedirectEnvironment;
5
+ gatewayOrigin: string;
6
+ siteOrigin: string;
7
+ callbackRoute: string;
8
+ webOrigins?: readonly string[];
9
+ nativeRedirectUris?: readonly string[];
10
+ }
11
+
12
+ export interface AuthRedirectConfiguration {
13
+ providerCallbackUrl: string;
14
+ siteUrl: string;
15
+ redirectAllowList: readonly string[];
16
+ serializedRedirectAllowList: string;
17
+ }
18
+
19
+ export function resolveAuthRedirectConfiguration(
20
+ input: ResolveAuthRedirectConfigurationInput,
21
+ ): AuthRedirectConfiguration {
22
+ const gatewayOrigin = normalizeWebOrigin(input.gatewayOrigin, 'gatewayOrigin');
23
+ const siteUrl = normalizeWebOrigin(input.siteOrigin, 'siteOrigin');
24
+ const callbackRoute = normalizeCallbackRoute(input.callbackRoute);
25
+ const webOrigins = unique([
26
+ siteUrl,
27
+ ...(input.webOrigins ?? []).map((origin) => normalizeWebOrigin(origin, 'webOrigins')),
28
+ ]);
29
+ const nativeRedirectUris = unique(
30
+ (input.nativeRedirectUris ?? []).map((uri) => normalizeNativeRedirectUri(uri)),
31
+ );
32
+
33
+ const redirectAllowList = unique([
34
+ ...webOrigins,
35
+ ...webOrigins.map((origin) => `${origin}${callbackRoute}`),
36
+ ...(input.environment === 'local' ? getLocalAuthRedirectPatterns(callbackRoute) : []),
37
+ ...nativeRedirectUris,
38
+ ]);
39
+
40
+ return {
41
+ providerCallbackUrl: `${gatewayOrigin}/auth/v1/callback`,
42
+ siteUrl,
43
+ redirectAllowList,
44
+ serializedRedirectAllowList: redirectAllowList.join(','),
45
+ };
46
+ }
47
+
48
+ export function getLocalAuthRedirectPatterns(callbackRoute: string): readonly string[] {
49
+ const normalizedRoute = normalizeCallbackRoute(callbackRoute);
50
+ return [`http://127.0.0.1:*${normalizedRoute}`, `http://localhost:*${normalizedRoute}`];
51
+ }
52
+
53
+ export function normalizeAuthCallbackRoute(callbackRoute: string): string {
54
+ return normalizeCallbackRoute(callbackRoute);
55
+ }
56
+
57
+ function normalizeCallbackRoute(callbackRoute: string): string {
58
+ const trimmed = callbackRoute.trim();
59
+ if (!trimmed) {
60
+ throw new Error('Auth callback route must not be empty.');
61
+ }
62
+
63
+ const withLeadingSlash = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
64
+ const normalized = withLeadingSlash.replace(/\/{2,}/gu, '/');
65
+ if (normalized.includes('?') || normalized.includes('#')) {
66
+ throw new Error('Auth callback route must not include a query string or fragment.');
67
+ }
68
+ return normalized;
69
+ }
70
+
71
+ function normalizeWebOrigin(value: string, field: string): string {
72
+ const parsed = parseUrl(value, field);
73
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
74
+ throw new Error(`${field} must use http or https.`);
75
+ }
76
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
77
+ throw new Error(`${field} must be a canonical origin without credentials, query, or fragment.`);
78
+ }
79
+ if (parsed.pathname !== '/' && parsed.pathname !== '') {
80
+ throw new Error(`${field} must not include a path.`);
81
+ }
82
+ return parsed.origin;
83
+ }
84
+
85
+ function normalizeNativeRedirectUri(value: string): string {
86
+ const parsed = parseUrl(value, 'nativeRedirectUris');
87
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
88
+ throw new Error('nativeRedirectUris must use a non-HTTP application scheme.');
89
+ }
90
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) {
91
+ throw new Error(
92
+ 'nativeRedirectUris must not include credentials, query strings, or fragments.',
93
+ );
94
+ }
95
+ return parsed.toString();
96
+ }
97
+
98
+ function parseUrl(value: string, field: string): URL {
99
+ const trimmed = value.trim();
100
+ if (!trimmed) {
101
+ throw new Error(`${field} must not contain empty values.`);
102
+ }
103
+
104
+ try {
105
+ return new URL(trimmed);
106
+ } catch {
107
+ throw new Error(`${field} contains an invalid URL.`);
108
+ }
109
+ }
110
+
111
+ function unique(values: readonly string[]): string[] {
112
+ return [...new Set(values)];
113
+ }
package/src/index.ts CHANGED
@@ -8,6 +8,14 @@ import type {
8
8
 
9
9
  export type { ApiInfrastructureArtifacts } from './apiArtifacts';
10
10
  export { generateApiInfrastructureArtifacts } from './apiArtifacts';
11
+ export {
12
+ type AuthRedirectConfiguration,
13
+ type AuthRedirectEnvironment,
14
+ getLocalAuthRedirectPatterns,
15
+ normalizeAuthCallbackRoute,
16
+ resolveAuthRedirectConfiguration,
17
+ type ResolveAuthRedirectConfigurationInput,
18
+ } from './authRedirects';
11
19
  export {
12
20
  createInfraSecretStoreAdapter,
13
21
  type CreateInfraSecretStoreAdapterInput,