@fabrika/provider-zerops 0.0.26 → 0.3.1
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/package.json +18 -5
- package/src/api/client.ts +522 -0
- package/src/api/errors.ts +50 -0
- package/src/api/polling.ts +59 -0
- package/src/api/readers.ts +185 -0
- package/src/api/service-env-response.ts +34 -0
- package/src/api/types.ts +602 -0
- package/src/api.ts +29 -1378
- package/src/authoring.ts +7 -10
- package/src/cli-args.ts +0 -5
- package/src/cli.ts +3 -28
- package/src/collaborators.ts +2 -4
- package/src/compile.ts +75 -20
- package/src/control.ts +217 -447
- package/src/descriptor.ts +135 -0
- package/src/index.ts +24 -128
- package/src/installation/admin-options.ts +128 -0
- package/src/installation/admin.ts +123 -0
- package/src/installation/deploy-options.ts +155 -0
- package/src/installation/deploy.ts +668 -0
- package/src/installation/generated/platform-console-schema.ts +41 -0
- package/src/installation/generated/platform-proxy-manifest.ts +72 -0
- package/src/installation/hosts.ts +117 -0
- package/src/installation/index.ts +366 -0
- package/src/installation/init.ts +521 -0
- package/src/installation/install-collaborators.ts +28 -0
- package/src/installation/install-environment.ts +193 -0
- package/src/installation/install-options.ts +193 -0
- package/src/installation/install-project.ts +201 -0
- package/src/installation/install-provisioning.ts +185 -0
- package/src/installation/install.ts +286 -0
- package/src/installation/installation-source.ts +42 -0
- package/src/installation/log.ts +77 -0
- package/src/installation/machine-key.ts +65 -0
- package/src/installation/manifest.ts +95 -0
- package/src/installation/placement-options.ts +33 -0
- package/src/installation/platform-source.ts +106 -0
- package/src/installation/platform-upload.ts +34 -0
- package/src/installation/proxy-manifest.ts +113 -0
- package/src/installation/secrets.ts +79 -0
- package/src/installation/sidecar.ts +122 -0
- package/src/installation/source-options.ts +12 -0
- package/src/installation/templates/README.md +116 -0
- package/src/installation/templates/fabrika.ref +1 -0
- package/src/installation/templates/gitignore +3 -0
- package/src/installation/templates/platform.yml +144 -0
- package/src/installation/topology/artifacts.ts +141 -0
- package/src/installation/topology/index.ts +13 -0
- package/src/installation/topology/installation-schemas.ts +225 -0
- package/src/installation/topology/invariants.ts +86 -0
- package/src/installation/topology/setups.ts +442 -0
- package/src/installation/topology/topology.ts +501 -0
- package/src/installation/topology/validate.ts +26 -0
- package/src/installation/upgrade-options.ts +108 -0
- package/src/installation/upgrade-output.ts +31 -0
- package/src/installation/upgrade.ts +373 -0
- package/src/managed-env.ts +105 -0
- package/src/manifest.ts +208 -315
- package/src/namespace/claims.ts +77 -0
- package/src/namespace/codec.ts +179 -0
- package/src/namespace/constants.ts +30 -0
- package/src/namespace/errors.ts +60 -0
- package/src/namespace/manifest.ts +28 -0
- package/src/namespace/operator.ts +151 -0
- package/src/namespace/provisioning.ts +482 -0
- package/src/namespace/proxy-attempt.ts +220 -0
- package/src/namespace/proxy-source.ts +22 -0
- package/src/namespace/topology.ts +115 -0
- package/src/namespace/types.ts +114 -0
- package/src/namespace.ts +34 -1260
- package/src/plan.ts +21 -23
- package/src/provider.ts +399 -254
- package/src/service-references.ts +28 -0
- package/src/source/constants.ts +11 -0
- package/src/source/port.ts +49 -0
- package/src/source/types.ts +4 -0
- package/src/source/validation.ts +72 -0
- package/src/source.ts +2 -1220
- package/src/types.ts +8 -7
- package/src/version-attempt.ts +18 -0
- package/src/codec.ts +0 -108
- package/src/namespace-command.ts +0 -209
- package/src/source-connection.ts +0 -344
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `${<host>_<KEY>}` — Zerops resolves it across services regardless of `envIsolation`, so the host is the boundary.
|
|
3
|
+
* `${RUNTIME_<host>_<KEY>}`, the build-time bridge, counts as a reference to that host until measured otherwise.
|
|
4
|
+
*/
|
|
5
|
+
const SERVICE_REFERENCE = /\$\{(?:RUNTIME_)?([a-z0-9]{1,25})_[A-Za-z0-9_]+\}/g
|
|
6
|
+
|
|
7
|
+
/** Namespace services every app in it may reference; the proxy is deliberately not one of them. */
|
|
8
|
+
const SHARED_NAMESPACE_HOSTNAMES: readonly string[] = ['postgres']
|
|
9
|
+
|
|
10
|
+
/** Hosts named by cross-service references in `text`, deduplicated, in order of first appearance. */
|
|
11
|
+
export function serviceReferenceHosts(text: string): string[] {
|
|
12
|
+
const hosts: string[] = []
|
|
13
|
+
for (const match of text.matchAll(SERVICE_REFERENCE)) {
|
|
14
|
+
const host = match[1]
|
|
15
|
+
if (host !== undefined && !hosts.includes(host)) hosts.push(host)
|
|
16
|
+
}
|
|
17
|
+
return hosts
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The hosts a service of this app may reference: its own declared hostnames plus the namespace's shared services. */
|
|
21
|
+
export function allowedReferenceHosts(ownHostnames: readonly string[]): ReadonlySet<string> {
|
|
22
|
+
return new Set([...ownHostnames, ...SHARED_NAMESPACE_HOSTNAMES])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The first referenced host outside `allowed`, or null when every reference stays inside the app. */
|
|
26
|
+
export function foreignReferenceHost(text: string, allowed: ReadonlySet<string>): string | null {
|
|
27
|
+
return serviceReferenceHosts(text).find((host) => !allowed.has(host)) ?? null
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const MAX_ID_LENGTH = 128
|
|
2
|
+
|
|
3
|
+
export const SHA256_PATTERN = /^[a-f0-9]{64}$/
|
|
4
|
+
|
|
5
|
+
export const COMMIT_SHA_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/
|
|
6
|
+
|
|
7
|
+
export const ID_PATTERN = /^[A-Za-z0-9._:-]+$/
|
|
8
|
+
|
|
9
|
+
export const GITHUB_OWNER_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?$/
|
|
10
|
+
|
|
11
|
+
export const GITHUB_REPOSITORY_PATTERN = /^[a-z0-9._-]{1,100}$/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ZeropsSourceRepository } from './types'
|
|
2
|
+
|
|
3
|
+
/** A private source names its connection; an installation id alone never selects a credential (ADR-0029). */
|
|
4
|
+
export interface ZeropsSourcePrivateBinding {
|
|
5
|
+
readonly connectionId: string
|
|
6
|
+
readonly installationId: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ZeropsSourceResolveInput {
|
|
10
|
+
readonly repository: ZeropsSourceRepository
|
|
11
|
+
readonly requestedRef: string
|
|
12
|
+
readonly expectedCommitSha?: string
|
|
13
|
+
readonly privateBinding?: ZeropsSourcePrivateBinding
|
|
14
|
+
readonly descriptorSha256: string
|
|
15
|
+
readonly signal: AbortSignal
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ZeropsSourceResolution {
|
|
19
|
+
readonly commitSha: string
|
|
20
|
+
readonly descriptorSha256: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** ADR-0045: platform archives are anonymous, tree-prechecked, keep relative symlinks. */
|
|
24
|
+
export type ZeropsSourceArchive =
|
|
25
|
+
| { readonly kind: 'application'; readonly privateBinding?: ZeropsSourcePrivateBinding }
|
|
26
|
+
| { readonly kind: 'platform' }
|
|
27
|
+
|
|
28
|
+
export interface ZeropsSourceUploadInput {
|
|
29
|
+
readonly repository: ZeropsSourceRepository
|
|
30
|
+
readonly commitSha: string
|
|
31
|
+
readonly descriptorSha256: string
|
|
32
|
+
readonly archive: ZeropsSourceArchive
|
|
33
|
+
/** A credential in transit: never logged, persisted, or returned in an error. */
|
|
34
|
+
readonly uploadUrl: string
|
|
35
|
+
readonly signal: AbortSignal
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ZeropsSourceUploadResult {
|
|
39
|
+
readonly commitSha: string
|
|
40
|
+
readonly descriptorSha256: string
|
|
41
|
+
readonly entryCount: number
|
|
42
|
+
readonly expandedBytes: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The GitHub source the Zerops composition injects; Control implements it in process. Callers verify what it returns. */
|
|
46
|
+
export interface ZeropsSourcePort {
|
|
47
|
+
resolve(input: ZeropsSourceResolveInput): Promise<ZeropsSourceResolution>
|
|
48
|
+
upload(input: ZeropsSourceUploadInput): Promise<ZeropsSourceUploadResult>
|
|
49
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { COMMIT_SHA_PATTERN, GITHUB_OWNER_PATTERN, GITHUB_REPOSITORY_PATTERN, ID_PATTERN, MAX_ID_LENGTH, SHA256_PATTERN } from './constants'
|
|
3
|
+
import type { ZeropsSourceRepository } from './types'
|
|
4
|
+
|
|
5
|
+
// Default Zod diagnostics can include rejected values and unknown keys, both of which may be secrets.
|
|
6
|
+
export function parseSourceWire<T>(schema: z.ZodType<T, z.ZodTypeDef, unknown>, value: unknown, label: string): T {
|
|
7
|
+
const result = schema.safeParse(value)
|
|
8
|
+
if (result.success) return result.data
|
|
9
|
+
if (result.error.issues.some((issue) => issue.code === 'unrecognized_keys')) throw new Error(`${label} contains an unknown field`)
|
|
10
|
+
const domainIssue = result.error.issues.find((issue) => issue.code === 'custom')
|
|
11
|
+
throw new Error(`${label}: ${domainIssue?.message ?? 'invalid wire value'}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const identifierSchema = z.string().min(1).max(MAX_ID_LENGTH).regex(ID_PATTERN)
|
|
15
|
+
export const commitShaSchema = z.string().regex(COMMIT_SHA_PATTERN)
|
|
16
|
+
export const sha256Schema = z.string().regex(SHA256_PATTERN)
|
|
17
|
+
export const repositorySchema = z.object({
|
|
18
|
+
owner: z.string().min(1).max(39).regex(GITHUB_OWNER_PATTERN),
|
|
19
|
+
name: z.string().min(1).max(100).regex(GITHUB_REPOSITORY_PATTERN).refine((name) => name !== '.' && name !== '..', 'invalid repository name'),
|
|
20
|
+
}).strict()
|
|
21
|
+
|
|
22
|
+
export const rejectExplicitUndefined = (value: object, context: z.RefinementCtx): void => {
|
|
23
|
+
if (Object.values(value).some((entry) => entry === undefined)) {
|
|
24
|
+
context.addIssue({ code: 'custom', message: 'optional fields must be omitted instead of undefined' })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const repository = (value: unknown, label: string): ZeropsSourceRepository => parseSourceWire(repositorySchema, value, label)
|
|
29
|
+
|
|
30
|
+
export const isZeropsUploadDestination = (value: string): boolean => {
|
|
31
|
+
if (!value.startsWith('https://proxy.app-prg1.zerops.io/api/rest/object-storage/upload?')) return false
|
|
32
|
+
let url: URL
|
|
33
|
+
try {
|
|
34
|
+
url = new URL(value)
|
|
35
|
+
} catch {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
return url.protocol === 'https:'
|
|
39
|
+
&& url.hostname === 'proxy.app-prg1.zerops.io'
|
|
40
|
+
&& url.pathname === '/api/rest/object-storage/upload'
|
|
41
|
+
&& url.username === ''
|
|
42
|
+
&& url.password === ''
|
|
43
|
+
&& url.port === ''
|
|
44
|
+
&& url.hash === ''
|
|
45
|
+
&& url.search !== ''
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function normalizeZeropsSourceRepository(repoUrl: string): ZeropsSourceRepository {
|
|
49
|
+
if (/\s/u.test(repoUrl) || /\p{Cc}/u.test(repoUrl)) {
|
|
50
|
+
throw new Error('source repository URL must be a canonical GitHub repository URL')
|
|
51
|
+
}
|
|
52
|
+
const candidate = repoUrl.startsWith('github.com/') ? `https://${repoUrl}` : repoUrl
|
|
53
|
+
let parsed: URL
|
|
54
|
+
try {
|
|
55
|
+
parsed = new URL(candidate)
|
|
56
|
+
} catch {
|
|
57
|
+
throw new Error('source repository URL must be a canonical GitHub repository URL')
|
|
58
|
+
}
|
|
59
|
+
if (
|
|
60
|
+
parsed.protocol !== 'https:' || parsed.hostname !== 'github.com' || parsed.port !== '' || parsed.username !== '' || parsed.password !== ''
|
|
61
|
+
|| parsed.search !== '' || parsed.hash !== ''
|
|
62
|
+
) {
|
|
63
|
+
throw new Error('source repository URL must be an HTTPS GitHub URL without credentials, port, query, or fragment')
|
|
64
|
+
}
|
|
65
|
+
const segments = parsed.pathname.split('/')
|
|
66
|
+
if (segments.length !== 3 || segments[0] !== '') throw new Error('source repository URL must identify one GitHub repository')
|
|
67
|
+
const owner = segments[1]
|
|
68
|
+
const rawName = segments[2]
|
|
69
|
+
if (owner === undefined || rawName === undefined) throw new Error('source repository URL must identify one GitHub repository')
|
|
70
|
+
const name = rawName.endsWith('.git') ? rawName.slice(0, -4) : rawName
|
|
71
|
+
return repository({ owner: owner.toLowerCase(), name: name.toLowerCase() }, 'source repository')
|
|
72
|
+
}
|