@fabrika/provider-zerops 0.7.0 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabrika/provider-zerops",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,14 +43,14 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "zod": "^3.25.76",
46
- "@fabrika/auth": "0.7.0",
47
- "@fabrika/auth-core": "0.7.0",
48
- "@fabrika/email": "0.7.0",
49
- "@fabrika/iam-contract": "0.7.0",
50
- "@fabrika/installation-init": "0.7.0",
51
- "@fabrika/operations-contract": "0.7.0",
52
- "@fabrika/provider-contract": "0.7.0",
53
- "@fabrika/proxy-core": "0.7.0",
46
+ "@fabrika/auth": "0.8.0",
47
+ "@fabrika/auth-core": "0.8.0",
48
+ "@fabrika/email": "0.8.0",
49
+ "@fabrika/iam-contract": "0.8.0",
50
+ "@fabrika/installation-init": "0.8.0",
51
+ "@fabrika/operations-contract": "0.8.0",
52
+ "@fabrika/provider-contract": "0.8.0",
53
+ "@fabrika/proxy-core": "0.8.0",
54
54
  "yaml": "^2.9.1"
55
55
  }
56
56
  }
package/src/control.ts CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  type ZeropsNamespaceTarget,
49
49
  zeropsNamespaceTargetCodec,
50
50
  } from './namespace'
51
- import { assertNamespaceProxyReads, type OlderProxyRowReaders } from './namespace/proxy-reads'
51
+ import { assertNamespaceProxyReads, type OlderProxyReaders } from './namespace/proxy-reads'
52
52
  import { createZeropsDeployer, ZEROPS_VERSION_DEADLINE_MS } from './provider'
53
53
  import { allowedReferenceHosts, assertNoPlatformOnlyReference, foreignReferenceHost, serviceReferenceHosts } from './service-references'
54
54
  import { normalizeZeropsSourceRepository } from './source'
@@ -102,8 +102,8 @@ export interface ZeropsControlProviderOptions {
102
102
  readonly proxySource: ZeropsNamespaceProxySource
103
103
  readonly iamUrl: string
104
104
  readonly iamKey: string
105
- /** The row parsers of the older releases' proxies, which a namespace pinned to an older source may run. */
106
- readonly olderProxies: OlderProxyRowReaders
105
+ /** The manifest parsers of the older releases' proxies, which a namespace pinned to an older source may run. */
106
+ readonly olderProxies: OlderProxyReaders
107
107
  }
108
108
  }
109
109
 
@@ -340,6 +340,10 @@ export const createZeropsControlProvider = (options: ZeropsControlProviderOption
340
340
  }
341
341
  },
342
342
  resolveSource,
343
+ validateAccess: ({ environment, access }) => {
344
+ const placement = resolvedEnvironment(environment, { requireReady: true })
345
+ assertNamespaceProxyReads(placement.namespace.id, access, placement.namespaceTarget.proxySource, options.namespaces)
346
+ },
343
347
  deploy: async (input: ProviderDeployInput): Promise<ProviderDeployOutcome> => {
344
348
  input.signal.throwIfAborted()
345
349
  assertAuthoredManagedInputs({ variableNames: Object.keys(input.vars), secretNames: Object.keys(input.secrets) })
package/src/index.ts CHANGED
@@ -101,8 +101,8 @@ export type {
101
101
  ZeropsProxyRepublishInput,
102
102
  } from './namespace'
103
103
  export { postgresSchema as zeropsNamespacePostgresSchema } from './namespace/codec'
104
- export { rowsNeedingCurrentProxy } from './namespace/proxy-reads'
105
- export type { OlderProxyRowReader, OlderProxyRowReaders } from './namespace/proxy-reads'
104
+ export { entriesNeedingCurrentProxy } from './namespace/proxy-reads'
105
+ export type { OlderProxyReader, OlderProxyReaders } from './namespace/proxy-reads'
106
106
  export { buildZeropsPlan, resolveDeployHostname } from './plan'
107
107
  export type { ZeropsStep, ZeropsStepKind } from './plan'
108
108
  export { CANCELLED, createZeropsDeployer, interpolateManifest } from './provider'
@@ -1,35 +1,46 @@
1
1
  // What a namespace proxy pinned to an older source cannot read. Its manifest parser is strict: one row of a form it does
2
- // not know refuses the whole manifest, and every member of the namespace loses its proxy at the next republish. So a
3
- // deploy that would publish such a row is refused while the namespace's proxy source is not this control plane's own.
2
+ // not know, or a CSP directive it does not know, refuses the whole manifest and blocks the namespace's next republish.
3
+ // So a deploy that would publish either is refused before publication while the namespace's proxy source is not
4
+ // this control plane's own.
4
5
 
5
6
  import type { ProviderApprovedAccess } from '@fabrika/provider-contract'
7
+ import type { CspOrigins } from '@fabrika/proxy-core/headers'
6
8
  import { contractProxyTable, type ProxyEndpointRow } from '@fabrika/proxy-core/manifest'
7
9
  import type { ZeropsNamespaceProxySource } from './types'
8
10
 
9
- /** Whether the namespace proxy of one older platform release parses a manifest row: that release's own row parser. */
10
- export type OlderProxyRowReader = (row: ProxyEndpointRow) => boolean
11
+ /**
12
+ * What the namespace proxy of one older platform release parses of an application's manifest entry: a row, and the
13
+ * entry's CSP directives, each by that release's own parser.
14
+ */
15
+ export interface OlderProxyReader {
16
+ readonly row: (row: ProxyEndpointRow) => boolean
17
+ readonly csp: (csp: CspOrigins) => boolean
18
+ }
11
19
 
12
20
  /**
13
21
  * The namespace proxies of the older releases this platform serves (ADR-0062). Control holds their parsers, as it holds
14
- * their contract readers; a pin's release is not recorded, so a row any of them reads is one the pin may read.
22
+ * their contract readers; a pin's release is not recorded, so what any of them reads is what the pin may read.
15
23
  */
16
- export type OlderProxyRowReaders = readonly [OlderProxyRowReader, ...OlderProxyRowReader[]]
24
+ export type OlderProxyReaders = readonly [OlderProxyReader, ...OlderProxyReader[]]
17
25
 
18
- /** The rows of the approved contract's proxy table that no older release's proxy parses, as the namespace publishes them. */
19
- export function rowsNeedingCurrentProxy(access: ProviderApprovedAccess, olderProxies: OlderProxyRowReaders): string[] {
20
- return contractProxyTable(access.contract, access.contractDigest).endpoints
21
- .filter((row) => !olderProxies.some((reads) => reads(row)))
22
- .map((row) => row.id)
26
+ /**
27
+ * What of the approved contract's manifest entry no older release's proxy parses, as the namespace publishes it: each
28
+ * such row by its id, and `csp` when no older proxy parses the entry's CSP (`workerSrc`, which only this release knows).
29
+ */
30
+ export function entriesNeedingCurrentProxy(access: ProviderApprovedAccess, olderProxies: OlderProxyReaders): string[] {
31
+ const table = contractProxyTable(access.contract, access.contractDigest)
32
+ const rows = table.endpoints.filter((row) => !olderProxies.some((proxy) => proxy.row(row))).map((row) => row.id)
33
+ return olderProxies.some((proxy) => proxy.csp(table.csp)) ? rows : [...rows, 'csp']
23
34
  }
24
35
 
25
36
  /** This control plane's own proxy source, and the parsers of the older releases a pinned proxy may run. */
26
37
  export interface PlatformProxies {
27
38
  readonly proxySource: ZeropsNamespaceProxySource
28
- readonly olderProxies: OlderProxyRowReaders
39
+ readonly olderProxies: OlderProxyReaders
29
40
  }
30
41
 
31
42
  /**
32
- * Refuses a deploy whose approved contract holds a row the namespace's pinned proxy may not read: its source is not the
43
+ * Refuses a deploy whose approved contract holds a row or a CSP directive the namespace's pinned proxy may not read: its source is not the
33
44
  * one this control plane deploys new placements from. `platform` is absent where this control plane manages no
34
45
  * namespace proxy, which then has nothing to compare.
35
46
  */
@@ -42,7 +53,7 @@ export function assertNamespaceProxyReads(
42
53
  if (access === undefined || platform === undefined || pinned.commitSha === platform.proxySource.commitSha) {
43
54
  return
44
55
  }
45
- const rows = rowsNeedingCurrentProxy(access, platform.olderProxies)
56
+ const rows = entriesNeedingCurrentProxy(access, platform.olderProxies)
46
57
  if (rows.length === 0) {
47
58
  return
48
59
  }