@ankhorage/contracts 19.4.0 → 20.0.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,19 @@
1
1
  # @ankhorage/contracts
2
2
 
3
+ ## 20.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 1034b76: Replace the unrealizable continuous database backup intent with provider-neutral scheduled S3
8
+ backups.
9
+
10
+ ## 19.5.0
11
+
12
+ ### Minor Changes
13
+
14
+ - d078138: Add provider-neutral S3 persistence targets for continuous database backups and external Supabase
15
+ Storage backends.
16
+
3
17
  ## 19.4.0
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/contracts
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v19.2.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: v19.5.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)
7
7
 
8
8
  Serializable app, action, theme, auth, and secret-store contracts for Ankhorage.
9
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"isInfraEnvironmentSpec.d.ts","sourceRoot":"","sources":["../../src/infra/isInfraEnvironmentSpec.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAA0B,MAAM,wBAAwB,CAAC;AAU3F,8FAA8F;AAC9F,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAOpF"}
1
+ {"version":3,"file":"isInfraEnvironmentSpec.d.ts","sourceRoot":"","sources":["../../src/infra/isInfraEnvironmentSpec.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,oBAAoB,EAIrB,MAAM,wBAAwB,CAAC;AAUhC,8FAA8F;AAC9F,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAOpF"}
@@ -22,6 +22,7 @@ function isEnvironmentShape(value) {
22
22
  isInfraShape(database, {
23
23
  provider: (provider) => provider === 'supabase',
24
24
  tier: (tier) => tier === undefined || tier === 'dev' || tier === 'prod',
25
+ backup: (backup) => backup === undefined || isScheduledDatabaseBackup(backup),
25
26
  }),
26
27
  objectStorage: (storage) => storage === undefined || isObjectStorage(storage),
27
28
  auth: (auth) => auth === undefined || isInfraAuthSpec(auth),
@@ -81,6 +82,25 @@ function isPolicyFiles(value) {
81
82
  new Set(value.map((policy) => (isRecord(policy) ? policy.path : undefined))).size ===
82
83
  value.length);
83
84
  }
85
+ /*** Validate provider-neutral scheduled database backup intent. */
86
+ function isScheduledDatabaseBackup(value) {
87
+ return isInfraShape(value, {
88
+ mode: (mode) => mode === 'scheduled',
89
+ target: isS3PersistenceTarget,
90
+ intervalHours: (interval) => interval === undefined ||
91
+ (typeof interval === 'number' && Number.isInteger(interval) && interval > 0),
92
+ });
93
+ }
94
+ /*** Validate one portable S3-compatible persistence target without resolving credentials. */
95
+ function isS3PersistenceTarget(value) {
96
+ return isInfraShape(value, {
97
+ endpoint: isPublicHttpOrigin,
98
+ region: isNonEmptyString,
99
+ bucket: isNonEmptyString,
100
+ credentials: isInfraCredentialRef,
101
+ forcePathStyle: (forcePathStyle) => forcePathStyle === undefined || typeof forcePathStyle === 'boolean',
102
+ });
103
+ }
84
104
  /*** Object storage can vary independently from database/auth; there is no implicit auto provider. */
85
105
  function isObjectStorage(value) {
86
106
  if (!isRecord(value))
@@ -89,8 +109,13 @@ function isObjectStorage(value) {
89
109
  provider: (provider) => provider === value.provider,
90
110
  buckets: infraFields.optionalStrings,
91
111
  };
92
- if (value.provider === 'supabase')
93
- return isInfraShape(value, common);
112
+ if (value.provider === 'supabase') {
113
+ return isInfraShape(value, {
114
+ ...common,
115
+ provider: (provider) => provider === 'supabase',
116
+ backend: (backend) => backend === undefined || isS3PersistenceTarget(backend),
117
+ });
118
+ }
94
119
  return isInfraShape(value, {
95
120
  ...common,
96
121
  provider: (provider) => provider === 'r2',
@@ -1 +1 @@
1
- {"version":3,"file":"isInfraEnvironmentSpec.js","sourceRoot":"","sources":["../../src/infra/isInfraEnvironmentSpec.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRecord } from '@ankhorage/utility/object';\nimport { isNonEmptyString } from '@ankhorage/utility/string';\n\nimport type { InfraEnvironmentSpec, InfraObjectStorageSpec } from '../types/infraManifest';\nimport type { InfraShape } from '../types/infraValidation';\nimport { INFRA_ADAPTER_CATALOG } from './constants';\nimport { infraFields } from './infraFields';\nimport { isInfraAuthSpec } from './isInfraAuthSpec';\nimport { isInfraCredentialRef } from './isInfraCredentialRef';\nimport { isInfraDeploymentSpec } from './isInfraDeploymentSpec';\nimport { isInfraShape } from './isInfraShape';\nimport { isInfraWorkloadSpec } from './isInfraWorkloadSpec';\n\n/*** Validate sibling capabilities and their required relationships within one environment. */\nexport function isInfraEnvironmentSpec(value: unknown): value is InfraEnvironmentSpec {\n return (\n isEnvironmentShape(value) &&\n hasServiceDependencies(value) &&\n hasNetworkingRelationships(value) &&\n hasUniquePublishedPorts(value)\n );\n}\n\n/*** Check all environment field shapes before inspecting canonical dependency metadata. */\nfunction isEnvironmentShape(value: unknown): value is InfraEnvironmentSpec {\n return isInfraShape(value, {\n deployment: isInfraDeploymentSpec,\n database: (database) =>\n database === undefined ||\n isInfraShape(database, {\n provider: (provider) => provider === 'supabase',\n tier: (tier) => tier === undefined || tier === 'dev' || tier === 'prod',\n }),\n objectStorage: (storage) => storage === undefined || isObjectStorage(storage),\n auth: (auth) => auth === undefined || isInfraAuthSpec(auth),\n authz: (authz) =>\n authz === undefined ||\n isInfraShape(authz, {\n provider: (provider) => provider === 'cerbos',\n kind: (kind) => kind === 'ABAC' || kind === 'RBAC',\n policies: (policies) => policies === undefined || isPolicyFiles(policies),\n }),\n secretStore: (store) =>\n store === undefined ||\n isInfraShape(store, {\n provider: (provider) => provider === 'supabase-vault',\n schema: infraFields.optionalText,\n }),\n networking: (networking) =>\n networking === undefined ||\n isInfraShape(networking, {\n domain: infraFields.optionalText,\n publicBaseUrl: (publicBaseUrl) =>\n publicBaseUrl === undefined || isPublicHttpOrigin(publicBaseUrl),\n tls: (tls) =>\n tls === undefined ||\n isInfraShape(tls, {\n mode: (mode) => mode === 'acme-http-01',\n contactEmail: isNonEmptyString,\n }),\n }),\n workloads: (workloads) => workloads === undefined || isWorkloads(workloads),\n } satisfies InfraShape<InfraEnvironmentSpec>);\n}\n\n/*** Public workload configuration uses one canonical absolute HTTP(S) origin without path state. */\nfunction isPublicHttpOrigin(value: unknown): boolean {\n if (!isNonEmptyString(value)) return false;\n try {\n const url = new URL(value);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.origin === value;\n } catch {\n return false;\n }\n}\n\n/*** Automatic TLS requires one matching HTTPS origin and public DNS hostname. */\nfunction hasNetworkingRelationships(value: InfraEnvironmentSpec): boolean {\n const { networking } = value;\n if (networking?.tls === undefined) return true;\n if (networking.domain === undefined || networking.publicBaseUrl === undefined) return false;\n const url = new URL(networking.publicBaseUrl);\n return url.protocol === 'https:' && url.hostname === networking.domain;\n}\n\n/*** Validate portable, unique Cerbos policy files without host paths or traversal. */\nfunction isPolicyFiles(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.every((policy) =>\n isInfraShape(policy, {\n path: (path) =>\n isNonEmptyString(path) && !path.startsWith('/') && !path.split('/').includes('..'),\n content: isNonEmptyString,\n }),\n ) &&\n new Set(value.map((policy) => (isRecord(policy) ? policy.path : undefined))).size ===\n value.length\n );\n}\n\n/*** Object storage can vary independently from database/auth; there is no implicit auto provider. */\nfunction isObjectStorage(value: unknown): boolean {\n if (!isRecord(value)) return false;\n const common = {\n provider: (provider: unknown) => provider === value.provider,\n buckets: infraFields.optionalStrings,\n };\n if (value.provider === 'supabase') return isInfraShape(value, common);\n return isInfraShape(value, {\n ...common,\n provider: (provider) => provider === 'r2',\n accountId: infraFields.optionalText,\n credentials: (credentials) => credentials === undefined || isInfraCredentialRef(credentials),\n } satisfies InfraShape<Extract<InfraObjectStorageSpec, { readonly provider: 'r2' }>>);\n}\n\n/*** Duplicate desired workload identities cannot be reconciled safely. */\nfunction isWorkloads(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.every(isInfraWorkloadSpec) &&\n new Set(value.map((workload) => workload.id)).size === value.length\n );\n}\n\n/*** Prevent two workloads in one environment from claiming the same external listener. */\nfunction hasUniquePublishedPorts(value: InfraEnvironmentSpec): boolean {\n const ports = (value.workloads ?? []).flatMap((workload) =>\n (workload.ports ?? []).flatMap(({ publishedPort }) =>\n publishedPort === undefined ? [] : [publishedPort],\n ),\n );\n return new Set(ports).size === ports.length;\n}\n\n/*** Check catalog dependencies against selected sibling capabilities and runtime capabilities. */\nfunction hasServiceDependencies(value: InfraEnvironmentSpec): boolean {\n const selections = [\n value.database,\n value.auth,\n value.authz,\n value.objectStorage,\n value.secretStore,\n ];\n const runtime = Object.values(INFRA_ADAPTER_CATALOG).find(\n (entry) => entry.id === value.deployment.runtime.provider,\n );\n const capabilities = new Set<string>([\n 'compute',\n ...(runtime?.capabilities ?? []),\n ...Object.entries(value)\n .filter(([, selection]) => selection !== undefined)\n .map(([capability]) => capability),\n ]);\n return Object.values(INFRA_ADAPTER_CATALOG)\n .filter((entry) => selections.some((selection) => selection?.provider === entry.id))\n .every((entry) => entry.dependencies.every((dependency) => capabilities.has(dependency)));\n}\n"]}
1
+ {"version":3,"file":"isInfraEnvironmentSpec.js","sourceRoot":"","sources":["../../src/infra/isInfraEnvironmentSpec.ts"],"names":[],"mappings":"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","sourcesContent":["import { isRecord } from '@ankhorage/utility/object';\nimport { isNonEmptyString } from '@ankhorage/utility/string';\n\nimport type {\n InfraEnvironmentSpec,\n InfraObjectStorageSpec,\n InfraS3PersistenceTarget,\n InfraScheduledDatabaseBackupSpec,\n} from '../types/infraManifest';\nimport type { InfraShape } from '../types/infraValidation';\nimport { INFRA_ADAPTER_CATALOG } from './constants';\nimport { infraFields } from './infraFields';\nimport { isInfraAuthSpec } from './isInfraAuthSpec';\nimport { isInfraCredentialRef } from './isInfraCredentialRef';\nimport { isInfraDeploymentSpec } from './isInfraDeploymentSpec';\nimport { isInfraShape } from './isInfraShape';\nimport { isInfraWorkloadSpec } from './isInfraWorkloadSpec';\n\n/*** Validate sibling capabilities and their required relationships within one environment. */\nexport function isInfraEnvironmentSpec(value: unknown): value is InfraEnvironmentSpec {\n return (\n isEnvironmentShape(value) &&\n hasServiceDependencies(value) &&\n hasNetworkingRelationships(value) &&\n hasUniquePublishedPorts(value)\n );\n}\n\n/*** Check all environment field shapes before inspecting canonical dependency metadata. */\nfunction isEnvironmentShape(value: unknown): value is InfraEnvironmentSpec {\n return isInfraShape(value, {\n deployment: isInfraDeploymentSpec,\n database: (database) =>\n database === undefined ||\n isInfraShape(database, {\n provider: (provider) => provider === 'supabase',\n tier: (tier) => tier === undefined || tier === 'dev' || tier === 'prod',\n backup: (backup) => backup === undefined || isScheduledDatabaseBackup(backup),\n }),\n objectStorage: (storage) => storage === undefined || isObjectStorage(storage),\n auth: (auth) => auth === undefined || isInfraAuthSpec(auth),\n authz: (authz) =>\n authz === undefined ||\n isInfraShape(authz, {\n provider: (provider) => provider === 'cerbos',\n kind: (kind) => kind === 'ABAC' || kind === 'RBAC',\n policies: (policies) => policies === undefined || isPolicyFiles(policies),\n }),\n secretStore: (store) =>\n store === undefined ||\n isInfraShape(store, {\n provider: (provider) => provider === 'supabase-vault',\n schema: infraFields.optionalText,\n }),\n networking: (networking) =>\n networking === undefined ||\n isInfraShape(networking, {\n domain: infraFields.optionalText,\n publicBaseUrl: (publicBaseUrl) =>\n publicBaseUrl === undefined || isPublicHttpOrigin(publicBaseUrl),\n tls: (tls) =>\n tls === undefined ||\n isInfraShape(tls, {\n mode: (mode) => mode === 'acme-http-01',\n contactEmail: isNonEmptyString,\n }),\n }),\n workloads: (workloads) => workloads === undefined || isWorkloads(workloads),\n } satisfies InfraShape<InfraEnvironmentSpec>);\n}\n\n/*** Public workload configuration uses one canonical absolute HTTP(S) origin without path state. */\nfunction isPublicHttpOrigin(value: unknown): boolean {\n if (!isNonEmptyString(value)) return false;\n try {\n const url = new URL(value);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.origin === value;\n } catch {\n return false;\n }\n}\n\n/*** Automatic TLS requires one matching HTTPS origin and public DNS hostname. */\nfunction hasNetworkingRelationships(value: InfraEnvironmentSpec): boolean {\n const { networking } = value;\n if (networking?.tls === undefined) return true;\n if (networking.domain === undefined || networking.publicBaseUrl === undefined) return false;\n const url = new URL(networking.publicBaseUrl);\n return url.protocol === 'https:' && url.hostname === networking.domain;\n}\n\n/*** Validate portable, unique Cerbos policy files without host paths or traversal. */\nfunction isPolicyFiles(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.every((policy) =>\n isInfraShape(policy, {\n path: (path) =>\n isNonEmptyString(path) && !path.startsWith('/') && !path.split('/').includes('..'),\n content: isNonEmptyString,\n }),\n ) &&\n new Set(value.map((policy) => (isRecord(policy) ? policy.path : undefined))).size ===\n value.length\n );\n}\n\n/*** Validate provider-neutral scheduled database backup intent. */\nfunction isScheduledDatabaseBackup(value: unknown): value is InfraScheduledDatabaseBackupSpec {\n return isInfraShape(value, {\n mode: (mode) => mode === 'scheduled',\n target: isS3PersistenceTarget,\n intervalHours: (interval) =>\n interval === undefined ||\n (typeof interval === 'number' && Number.isInteger(interval) && interval > 0),\n });\n}\n\n/*** Validate one portable S3-compatible persistence target without resolving credentials. */\nfunction isS3PersistenceTarget(value: unknown): value is InfraS3PersistenceTarget {\n return isInfraShape(value, {\n endpoint: isPublicHttpOrigin,\n region: isNonEmptyString,\n bucket: isNonEmptyString,\n credentials: isInfraCredentialRef,\n forcePathStyle: (forcePathStyle) =>\n forcePathStyle === undefined || typeof forcePathStyle === 'boolean',\n });\n}\n\n/*** Object storage can vary independently from database/auth; there is no implicit auto provider. */\nfunction isObjectStorage(value: unknown): boolean {\n if (!isRecord(value)) return false;\n const common = {\n provider: (provider: unknown) => provider === value.provider,\n buckets: infraFields.optionalStrings,\n };\n if (value.provider === 'supabase') {\n return isInfraShape(value, {\n ...common,\n provider: (provider) => provider === 'supabase',\n backend: (backend) => backend === undefined || isS3PersistenceTarget(backend),\n } satisfies InfraShape<Extract<InfraObjectStorageSpec, { readonly provider: 'supabase' }>>);\n }\n return isInfraShape(value, {\n ...common,\n provider: (provider) => provider === 'r2',\n accountId: infraFields.optionalText,\n credentials: (credentials) => credentials === undefined || isInfraCredentialRef(credentials),\n } satisfies InfraShape<Extract<InfraObjectStorageSpec, { readonly provider: 'r2' }>>);\n}\n\n/*** Duplicate desired workload identities cannot be reconciled safely. */\nfunction isWorkloads(value: unknown): boolean {\n return (\n Array.isArray(value) &&\n value.every(isInfraWorkloadSpec) &&\n new Set(value.map((workload) => workload.id)).size === value.length\n );\n}\n\n/*** Prevent two workloads in one environment from claiming the same external listener. */\nfunction hasUniquePublishedPorts(value: InfraEnvironmentSpec): boolean {\n const ports = (value.workloads ?? []).flatMap((workload) =>\n (workload.ports ?? []).flatMap(({ publishedPort }) =>\n publishedPort === undefined ? [] : [publishedPort],\n ),\n );\n return new Set(ports).size === ports.length;\n}\n\n/*** Check catalog dependencies against selected sibling capabilities and runtime capabilities. */\nfunction hasServiceDependencies(value: InfraEnvironmentSpec): boolean {\n const selections = [\n value.database,\n value.auth,\n value.authz,\n value.objectStorage,\n value.secretStore,\n ];\n const runtime = Object.values(INFRA_ADAPTER_CATALOG).find(\n (entry) => entry.id === value.deployment.runtime.provider,\n );\n const capabilities = new Set<string>([\n 'compute',\n ...(runtime?.capabilities ?? []),\n ...Object.entries(value)\n .filter(([, selection]) => selection !== undefined)\n .map(([capability]) => capability),\n ]);\n return Object.values(INFRA_ADAPTER_CATALOG)\n .filter((entry) => selections.some((selection) => selection?.provider === entry.id))\n .every((entry) => entry.dependencies.every((dependency) => capabilities.has(dependency)));\n}\n"]}
@@ -64,14 +64,30 @@ export type InfraDeploymentSpec = {
64
64
  readonly runtime: InfraRuntimeSelection<R>;
65
65
  };
66
66
  }[InfraRuntimeProviderId];
67
+ /** Portable S3-compatible persistence target; privileged values stay behind one credential ref. */
68
+ export interface InfraS3PersistenceTarget {
69
+ readonly endpoint: string;
70
+ readonly region: string;
71
+ readonly bucket: string;
72
+ readonly credentials: InfraControlPlaneCredentialRef;
73
+ readonly forcePathStyle?: boolean;
74
+ }
75
+ /** Scheduled database protection intent; the selected database provider owns its concrete engine. */
76
+ export interface InfraScheduledDatabaseBackupSpec {
77
+ readonly mode: 'scheduled';
78
+ readonly target: InfraS3PersistenceTarget;
79
+ readonly intervalHours?: number;
80
+ }
67
81
  export interface InfraDatabaseConfigMap {
68
82
  readonly supabase: {
69
83
  readonly tier?: 'dev' | 'prod';
84
+ readonly backup?: InfraScheduledDatabaseBackupSpec;
70
85
  };
71
86
  }
72
87
  export interface InfraObjectStorageConfigMap {
73
88
  readonly supabase: {
74
89
  readonly buckets?: readonly string[];
90
+ readonly backend?: InfraS3PersistenceTarget;
75
91
  };
76
92
  readonly r2: {
77
93
  readonly buckets?: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"infraManifest.d.ts","sourceRoot":"","sources":["../../src/types/infraManifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"infraManifest.d.ts","sourceRoot":"","sources":["../../src/types/infraManifest.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"infraManifest.js","sourceRoot":"","sources":["../../src/types/infraManifest.ts"],"names":[],"mappings":"","sourcesContent":["import type { AuthFlowConfig, AuthOAuthConfig } from '../auth';\nimport type { ApiDefinitionList } from '../data';\nimport type { AppEnvironmentId } from '../environments';\nimport type { INFRA_ADAPTER_CATALOG, INFRA_RUNTIME_COMPATIBILITY } from '../infra/constants';\nimport type { AuthProfileSpec, AuthScope, AuthSignInSpec, AuthSignUpSpec } from '../types';\nimport type { InfraControlPlaneCredentialRef } from './infraSecrets';\nimport type { InfraWorkloadSpec } from './infraWorkload';\n\nexport type InfraAdapterId = keyof typeof INFRA_ADAPTER_CATALOG;\nexport type InfraCapability =\n (typeof INFRA_ADAPTER_CATALOG)[InfraAdapterId]['capabilities'][number];\nexport type InfraProviderFor<C extends InfraCapability> = {\n [P in InfraAdapterId]: C extends (typeof INFRA_ADAPTER_CATALOG)[P]['capabilities'][number]\n ? P\n : never;\n}[InfraAdapterId];\nexport type InfraComputeProviderId = InfraProviderFor<'compute'>;\nexport type InfraRuntimeProviderId = keyof typeof INFRA_RUNTIME_COMPATIBILITY;\n\n/** Non-secret provider configuration; credentials are resolved only at the execution boundary. */\nexport interface InfraComputeConfigMap {\n readonly local: { readonly workingDirectory?: string };\n readonly hetzner: {\n readonly location: string;\n readonly serverType?: string;\n readonly image?: string;\n readonly credentials?: InfraControlPlaneCredentialRef;\n readonly ssh?: {\n readonly user?: string;\n readonly port?: number;\n readonly credentials?: InfraControlPlaneCredentialRef;\n };\n };\n}\n\nexport interface InfraRuntimeConfigMap {\n readonly minikube: {\n readonly profile?: string;\n readonly driver?: 'docker' | 'podman';\n readonly cpus?: number;\n readonly memoryMiB?: number;\n };\n readonly k3s: {\n readonly version?: string;\n readonly topology?: { readonly servers: number; readonly agents: number };\n };\n readonly 'docker-compose': { readonly projectName?: string };\n}\n\nexport type InfraComputeSelection<P extends InfraComputeProviderId = InfraComputeProviderId> = {\n [K in P]: { readonly provider: K } & InfraComputeConfigMap[K];\n}[P];\nexport type InfraRuntimeSelection<P extends InfraRuntimeProviderId = InfraRuntimeProviderId> = {\n [K in P]: { readonly provider: K } & InfraRuntimeConfigMap[K];\n}[P];\n\n/** A discriminated union derived from the runtime catalog, never a Cartesian product. */\nexport type InfraDeploymentSpec = {\n [R in InfraRuntimeProviderId]: {\n readonly compute: InfraComputeSelection<(typeof INFRA_RUNTIME_COMPATIBILITY)[R][number]>;\n readonly runtime: InfraRuntimeSelection<R>;\n };\n}[InfraRuntimeProviderId];\n\nexport interface InfraDatabaseConfigMap {\n readonly supabase: { readonly tier?: 'dev' | 'prod' };\n}\nexport interface InfraObjectStorageConfigMap {\n readonly supabase: { readonly buckets?: readonly string[] };\n readonly r2: {\n readonly buckets?: readonly string[];\n readonly accountId?: string;\n readonly credentials?: InfraControlPlaneCredentialRef;\n };\n}\nexport interface InfraAuthConfigMap {\n readonly supabase: {\n readonly scope?: AuthScope;\n readonly flow?: AuthFlowConfig;\n readonly signIn?: AuthSignInSpec;\n readonly signUp?: AuthSignUpSpec;\n readonly oauth?: AuthOAuthConfig;\n readonly profile?: AuthProfileSpec;\n };\n}\nexport interface InfraAuthzConfigMap {\n readonly cerbos: {\n readonly kind: 'RBAC' | 'ABAC';\n readonly policies?: readonly InfraAuthzPolicyFile[];\n };\n}\nexport interface InfraAuthzPolicyFile {\n readonly path: string;\n readonly content: string;\n}\nexport interface InfraSecretStoreConfigMap {\n readonly 'supabase-vault': { readonly schema?: string };\n}\n\nexport type InfraDatabaseSpec = {\n [P in InfraProviderFor<'database'>]: { readonly provider: P } & InfraDatabaseConfigMap[P];\n}[InfraProviderFor<'database'>];\nexport type InfraObjectStorageSpec = {\n [P in InfraProviderFor<'objectStorage'>]: {\n readonly provider: P;\n } & InfraObjectStorageConfigMap[P];\n}[InfraProviderFor<'objectStorage'>];\nexport type InfraAuthSpec = {\n [P in InfraProviderFor<'auth'>]: { readonly provider: P } & InfraAuthConfigMap[P];\n}[InfraProviderFor<'auth'>];\nexport type InfraAuthzSpec = {\n [P in InfraProviderFor<'authz'>]: { readonly provider: P } & InfraAuthzConfigMap[P];\n}[InfraProviderFor<'authz'>];\nexport type InfraSecretStoreSpec = {\n [P in InfraProviderFor<'secretStore'>]: { readonly provider: P } & InfraSecretStoreConfigMap[P];\n}[InfraProviderFor<'secretStore'>];\n\nexport interface InfraNetworkingTlsSpec {\n /** First production slice: runtime-managed ACME using the HTTP-01 challenge. */\n readonly mode: 'acme-http-01';\n /** ACME registration and expiry-notification contact. */\n readonly contactEmail: string;\n}\n\nexport interface InfraNetworkingSpec {\n /** Public DNS name intent, not automatic DNS/CDN vendor provisioning. */\n readonly domain?: string;\n /** Absolute HTTP(S) origin used by workloads that must know their external URL at startup. */\n readonly publicBaseUrl?: string;\n /** Runtime-neutral automatic TLS intent; the selected runtime owns concrete ingress configuration. */\n readonly tls?: InfraNetworkingTlsSpec;\n}\n\nexport interface InfraEnvironmentSpec {\n readonly deployment: InfraDeploymentSpec;\n readonly database?: InfraDatabaseSpec;\n readonly objectStorage?: InfraObjectStorageSpec;\n readonly auth?: InfraAuthSpec;\n readonly authz?: InfraAuthzSpec;\n readonly secretStore?: InfraSecretStoreSpec;\n readonly networking?: InfraNetworkingSpec;\n readonly workloads?: readonly InfraWorkloadSpec[];\n}\n\nexport interface InfraManifest {\n readonly environments: Readonly<\n Record<'local', InfraEnvironmentSpec> &\n Partial<Record<Exclude<AppEnvironmentId, 'local'>, InfraEnvironmentSpec>>\n >;\n readonly apis?: ApiDefinitionList;\n readonly modules: readonly string[];\n readonly modulesConfig?: Readonly<Record<string, unknown>>;\n}\n"]}
1
+ {"version":3,"file":"infraManifest.js","sourceRoot":"","sources":["../../src/types/infraManifest.ts"],"names":[],"mappings":"","sourcesContent":["import type { AuthFlowConfig, AuthOAuthConfig } from '../auth';\nimport type { ApiDefinitionList } from '../data';\nimport type { AppEnvironmentId } from '../environments';\nimport type { INFRA_ADAPTER_CATALOG, INFRA_RUNTIME_COMPATIBILITY } from '../infra/constants';\nimport type { AuthProfileSpec, AuthScope, AuthSignInSpec, AuthSignUpSpec } from '../types';\nimport type { InfraControlPlaneCredentialRef } from './infraSecrets';\nimport type { InfraWorkloadSpec } from './infraWorkload';\n\nexport type InfraAdapterId = keyof typeof INFRA_ADAPTER_CATALOG;\nexport type InfraCapability =\n (typeof INFRA_ADAPTER_CATALOG)[InfraAdapterId]['capabilities'][number];\nexport type InfraProviderFor<C extends InfraCapability> = {\n [P in InfraAdapterId]: C extends (typeof INFRA_ADAPTER_CATALOG)[P]['capabilities'][number]\n ? P\n : never;\n}[InfraAdapterId];\nexport type InfraComputeProviderId = InfraProviderFor<'compute'>;\nexport type InfraRuntimeProviderId = keyof typeof INFRA_RUNTIME_COMPATIBILITY;\n\n/** Non-secret provider configuration; credentials are resolved only at the execution boundary. */\nexport interface InfraComputeConfigMap {\n readonly local: { readonly workingDirectory?: string };\n readonly hetzner: {\n readonly location: string;\n readonly serverType?: string;\n readonly image?: string;\n readonly credentials?: InfraControlPlaneCredentialRef;\n readonly ssh?: {\n readonly user?: string;\n readonly port?: number;\n readonly credentials?: InfraControlPlaneCredentialRef;\n };\n };\n}\n\nexport interface InfraRuntimeConfigMap {\n readonly minikube: {\n readonly profile?: string;\n readonly driver?: 'docker' | 'podman';\n readonly cpus?: number;\n readonly memoryMiB?: number;\n };\n readonly k3s: {\n readonly version?: string;\n readonly topology?: { readonly servers: number; readonly agents: number };\n };\n readonly 'docker-compose': { readonly projectName?: string };\n}\n\nexport type InfraComputeSelection<P extends InfraComputeProviderId = InfraComputeProviderId> = {\n [K in P]: { readonly provider: K } & InfraComputeConfigMap[K];\n}[P];\nexport type InfraRuntimeSelection<P extends InfraRuntimeProviderId = InfraRuntimeProviderId> = {\n [K in P]: { readonly provider: K } & InfraRuntimeConfigMap[K];\n}[P];\n\n/** A discriminated union derived from the runtime catalog, never a Cartesian product. */\nexport type InfraDeploymentSpec = {\n [R in InfraRuntimeProviderId]: {\n readonly compute: InfraComputeSelection<(typeof INFRA_RUNTIME_COMPATIBILITY)[R][number]>;\n readonly runtime: InfraRuntimeSelection<R>;\n };\n}[InfraRuntimeProviderId];\n\n/** Portable S3-compatible persistence target; privileged values stay behind one credential ref. */\nexport interface InfraS3PersistenceTarget {\n readonly endpoint: string;\n readonly region: string;\n readonly bucket: string;\n readonly credentials: InfraControlPlaneCredentialRef;\n readonly forcePathStyle?: boolean;\n}\n\n/** Scheduled database protection intent; the selected database provider owns its concrete engine. */\nexport interface InfraScheduledDatabaseBackupSpec {\n readonly mode: 'scheduled';\n readonly target: InfraS3PersistenceTarget;\n readonly intervalHours?: number;\n}\n\nexport interface InfraDatabaseConfigMap {\n readonly supabase: {\n readonly tier?: 'dev' | 'prod';\n readonly backup?: InfraScheduledDatabaseBackupSpec;\n };\n}\nexport interface InfraObjectStorageConfigMap {\n readonly supabase: {\n readonly buckets?: readonly string[];\n readonly backend?: InfraS3PersistenceTarget;\n };\n readonly r2: {\n readonly buckets?: readonly string[];\n readonly accountId?: string;\n readonly credentials?: InfraControlPlaneCredentialRef;\n };\n}\nexport interface InfraAuthConfigMap {\n readonly supabase: {\n readonly scope?: AuthScope;\n readonly flow?: AuthFlowConfig;\n readonly signIn?: AuthSignInSpec;\n readonly signUp?: AuthSignUpSpec;\n readonly oauth?: AuthOAuthConfig;\n readonly profile?: AuthProfileSpec;\n };\n}\nexport interface InfraAuthzConfigMap {\n readonly cerbos: {\n readonly kind: 'RBAC' | 'ABAC';\n readonly policies?: readonly InfraAuthzPolicyFile[];\n };\n}\nexport interface InfraAuthzPolicyFile {\n readonly path: string;\n readonly content: string;\n}\nexport interface InfraSecretStoreConfigMap {\n readonly 'supabase-vault': { readonly schema?: string };\n}\n\nexport type InfraDatabaseSpec = {\n [P in InfraProviderFor<'database'>]: { readonly provider: P } & InfraDatabaseConfigMap[P];\n}[InfraProviderFor<'database'>];\nexport type InfraObjectStorageSpec = {\n [P in InfraProviderFor<'objectStorage'>]: {\n readonly provider: P;\n } & InfraObjectStorageConfigMap[P];\n}[InfraProviderFor<'objectStorage'>];\nexport type InfraAuthSpec = {\n [P in InfraProviderFor<'auth'>]: { readonly provider: P } & InfraAuthConfigMap[P];\n}[InfraProviderFor<'auth'>];\nexport type InfraAuthzSpec = {\n [P in InfraProviderFor<'authz'>]: { readonly provider: P } & InfraAuthzConfigMap[P];\n}[InfraProviderFor<'authz'>];\nexport type InfraSecretStoreSpec = {\n [P in InfraProviderFor<'secretStore'>]: { readonly provider: P } & InfraSecretStoreConfigMap[P];\n}[InfraProviderFor<'secretStore'>];\n\nexport interface InfraNetworkingTlsSpec {\n /** First production slice: runtime-managed ACME using the HTTP-01 challenge. */\n readonly mode: 'acme-http-01';\n /** ACME registration and expiry-notification contact. */\n readonly contactEmail: string;\n}\n\nexport interface InfraNetworkingSpec {\n /** Public DNS name intent, not automatic DNS/CDN vendor provisioning. */\n readonly domain?: string;\n /** Absolute HTTP(S) origin used by workloads that must know their external URL at startup. */\n readonly publicBaseUrl?: string;\n /** Runtime-neutral automatic TLS intent; the selected runtime owns concrete ingress configuration. */\n readonly tls?: InfraNetworkingTlsSpec;\n}\n\nexport interface InfraEnvironmentSpec {\n readonly deployment: InfraDeploymentSpec;\n readonly database?: InfraDatabaseSpec;\n readonly objectStorage?: InfraObjectStorageSpec;\n readonly auth?: InfraAuthSpec;\n readonly authz?: InfraAuthzSpec;\n readonly secretStore?: InfraSecretStoreSpec;\n readonly networking?: InfraNetworkingSpec;\n readonly workloads?: readonly InfraWorkloadSpec[];\n}\n\nexport interface InfraManifest {\n readonly environments: Readonly<\n Record<'local', InfraEnvironmentSpec> &\n Partial<Record<Exclude<AppEnvironmentId, 'local'>, InfraEnvironmentSpec>>\n >;\n readonly apis?: ApiDefinitionList;\n readonly modules: readonly string[];\n readonly modulesConfig?: Readonly<Record<string, unknown>>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/contracts",
3
- "version": "19.4.0",
3
+ "version": "20.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "ankh": {
6
6
  "category": "contracts",
@@ -1,7 +1,12 @@
1
1
  import { isRecord } from '@ankhorage/utility/object';
2
2
  import { isNonEmptyString } from '@ankhorage/utility/string';
3
3
 
4
- import type { InfraEnvironmentSpec, InfraObjectStorageSpec } from '../types/infraManifest';
4
+ import type {
5
+ InfraEnvironmentSpec,
6
+ InfraObjectStorageSpec,
7
+ InfraS3PersistenceTarget,
8
+ InfraScheduledDatabaseBackupSpec,
9
+ } from '../types/infraManifest';
5
10
  import type { InfraShape } from '../types/infraValidation';
6
11
  import { INFRA_ADAPTER_CATALOG } from './constants';
7
12
  import { infraFields } from './infraFields';
@@ -30,6 +35,7 @@ function isEnvironmentShape(value: unknown): value is InfraEnvironmentSpec {
30
35
  isInfraShape(database, {
31
36
  provider: (provider) => provider === 'supabase',
32
37
  tier: (tier) => tier === undefined || tier === 'dev' || tier === 'prod',
38
+ backup: (backup) => backup === undefined || isScheduledDatabaseBackup(backup),
33
39
  }),
34
40
  objectStorage: (storage) => storage === undefined || isObjectStorage(storage),
35
41
  auth: (auth) => auth === undefined || isInfraAuthSpec(auth),
@@ -99,6 +105,29 @@ function isPolicyFiles(value: unknown): boolean {
99
105
  );
100
106
  }
101
107
 
108
+ /*** Validate provider-neutral scheduled database backup intent. */
109
+ function isScheduledDatabaseBackup(value: unknown): value is InfraScheduledDatabaseBackupSpec {
110
+ return isInfraShape(value, {
111
+ mode: (mode) => mode === 'scheduled',
112
+ target: isS3PersistenceTarget,
113
+ intervalHours: (interval) =>
114
+ interval === undefined ||
115
+ (typeof interval === 'number' && Number.isInteger(interval) && interval > 0),
116
+ });
117
+ }
118
+
119
+ /*** Validate one portable S3-compatible persistence target without resolving credentials. */
120
+ function isS3PersistenceTarget(value: unknown): value is InfraS3PersistenceTarget {
121
+ return isInfraShape(value, {
122
+ endpoint: isPublicHttpOrigin,
123
+ region: isNonEmptyString,
124
+ bucket: isNonEmptyString,
125
+ credentials: isInfraCredentialRef,
126
+ forcePathStyle: (forcePathStyle) =>
127
+ forcePathStyle === undefined || typeof forcePathStyle === 'boolean',
128
+ });
129
+ }
130
+
102
131
  /*** Object storage can vary independently from database/auth; there is no implicit auto provider. */
103
132
  function isObjectStorage(value: unknown): boolean {
104
133
  if (!isRecord(value)) return false;
@@ -106,7 +135,13 @@ function isObjectStorage(value: unknown): boolean {
106
135
  provider: (provider: unknown) => provider === value.provider,
107
136
  buckets: infraFields.optionalStrings,
108
137
  };
109
- if (value.provider === 'supabase') return isInfraShape(value, common);
138
+ if (value.provider === 'supabase') {
139
+ return isInfraShape(value, {
140
+ ...common,
141
+ provider: (provider) => provider === 'supabase',
142
+ backend: (backend) => backend === undefined || isS3PersistenceTarget(backend),
143
+ } satisfies InfraShape<Extract<InfraObjectStorageSpec, { readonly provider: 'supabase' }>>);
144
+ }
110
145
  return isInfraShape(value, {
111
146
  ...common,
112
147
  provider: (provider) => provider === 'r2',
@@ -0,0 +1,90 @@
1
+ import { expect, it } from 'bun:test';
2
+
3
+ import { isInfraEnvironmentSpec } from './infra';
4
+
5
+ const deployment = {
6
+ compute: { provider: 'local' },
7
+ runtime: { provider: 'minikube' },
8
+ } as const;
9
+ const credentials = { source: 'control-plane', name: 'S3_PERSISTENCE' } as const;
10
+ const target = {
11
+ endpoint: 'https://s3.example.com',
12
+ region: 'eu-central-1',
13
+ bucket: 'ankhorage-backups',
14
+ credentials,
15
+ forcePathStyle: true,
16
+ } as const;
17
+
18
+ function environment(overrides: Readonly<Record<string, unknown>> = {}): unknown {
19
+ return { deployment, ...overrides };
20
+ }
21
+
22
+ it('accepts scheduled database backups and an S3-backed Supabase storage service', () => {
23
+ expect(
24
+ isInfraEnvironmentSpec(
25
+ environment({
26
+ database: {
27
+ provider: 'supabase',
28
+ tier: 'prod',
29
+ backup: {
30
+ mode: 'scheduled',
31
+ target,
32
+ intervalHours: 24,
33
+ },
34
+ },
35
+ objectStorage: {
36
+ provider: 'supabase',
37
+ buckets: ['media'],
38
+ backend: { ...target, bucket: 'ankhorage-storage' },
39
+ },
40
+ }),
41
+ ),
42
+ ).toBe(true);
43
+ });
44
+
45
+ it.each([
46
+ { target: { ...target, endpoint: '/relative' } },
47
+ { target: { ...target, region: '' } },
48
+ { target: { ...target, bucket: '' } },
49
+ { target: { ...target, credentials: { source: 'secret-store', name: 'S3' } } },
50
+ { target, intervalHours: 0 },
51
+ { target, intervalHours: 1.5 },
52
+ { target, intervalHours: Number.NaN },
53
+ ])('rejects malformed scheduled database backup intent: %j', (backup) => {
54
+ expect(
55
+ isInfraEnvironmentSpec(
56
+ environment({
57
+ database: {
58
+ provider: 'supabase',
59
+ backup: { mode: 'scheduled', ...backup },
60
+ },
61
+ }),
62
+ ),
63
+ ).toBe(false);
64
+ });
65
+
66
+ it('rejects the unsupported continuous database backup mode', () => {
67
+ expect(
68
+ isInfraEnvironmentSpec(
69
+ environment({
70
+ database: {
71
+ provider: 'supabase',
72
+ backup: { mode: 'continuous', target, baseBackupIntervalHours: 24 },
73
+ },
74
+ }),
75
+ ),
76
+ ).toBe(false);
77
+ });
78
+
79
+ it('rejects malformed S3-backed object storage configuration', () => {
80
+ expect(
81
+ isInfraEnvironmentSpec(
82
+ environment({
83
+ objectStorage: {
84
+ provider: 'supabase',
85
+ backend: { ...target, forcePathStyle: 'true' },
86
+ },
87
+ }),
88
+ ),
89
+ ).toBe(false);
90
+ });
@@ -62,11 +62,33 @@ export type InfraDeploymentSpec = {
62
62
  };
63
63
  }[InfraRuntimeProviderId];
64
64
 
65
+ /** Portable S3-compatible persistence target; privileged values stay behind one credential ref. */
66
+ export interface InfraS3PersistenceTarget {
67
+ readonly endpoint: string;
68
+ readonly region: string;
69
+ readonly bucket: string;
70
+ readonly credentials: InfraControlPlaneCredentialRef;
71
+ readonly forcePathStyle?: boolean;
72
+ }
73
+
74
+ /** Scheduled database protection intent; the selected database provider owns its concrete engine. */
75
+ export interface InfraScheduledDatabaseBackupSpec {
76
+ readonly mode: 'scheduled';
77
+ readonly target: InfraS3PersistenceTarget;
78
+ readonly intervalHours?: number;
79
+ }
80
+
65
81
  export interface InfraDatabaseConfigMap {
66
- readonly supabase: { readonly tier?: 'dev' | 'prod' };
82
+ readonly supabase: {
83
+ readonly tier?: 'dev' | 'prod';
84
+ readonly backup?: InfraScheduledDatabaseBackupSpec;
85
+ };
67
86
  }
68
87
  export interface InfraObjectStorageConfigMap {
69
- readonly supabase: { readonly buckets?: readonly string[] };
88
+ readonly supabase: {
89
+ readonly buckets?: readonly string[];
90
+ readonly backend?: InfraS3PersistenceTarget;
91
+ };
70
92
  readonly r2: {
71
93
  readonly buckets?: readonly string[];
72
94
  readonly accountId?: string;