@crombolo/contracts 0.14.1 → 0.15.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": "@crombolo/contracts",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "The Umbraco Management and Delivery API OpenAPI documents and the TypeScript types generated from them.",
5
5
  "license": "MIT",
6
6
  "author": "Crombolo",
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './overrides.ts'
1
2
  export * from './spec.ts'
2
3
  export type * from './types.ts'
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Where crombolo's Management API differs from Umbraco's contract on purpose.
3
+ * `OpenApi.json` stays exactly Umbraco's, so a re-copy from a new release is a
4
+ * clean diff; each difference is listed here with its reason, and applied when
5
+ * the contract is loaded and when it is served.
6
+ */
7
+ export interface ContractOverride {
8
+ /** The schema under `components.schemas` it changes. */
9
+ schema: string
10
+ story: string
11
+ reason: string
12
+ apply(schema: Record<string, unknown>): void
13
+ }
14
+
15
+ export const CONTRACT_OVERRIDES: readonly ContractOverride[] = [
16
+ {
17
+ schema: 'CreateMemberRequestModel',
18
+ story: '035',
19
+ reason:
20
+ 'A member who will only ever sign in through an identity provider is created with no password, as external sign-in creates one.',
21
+ apply(schema) {
22
+ schema.required = ((schema.required as string[] | undefined) ?? []).filter(
23
+ (field) => field !== 'password',
24
+ )
25
+ },
26
+ },
27
+ ]
28
+
29
+ /** The contract with every override applied, leaving the one given untouched. */
30
+ export function applyOverrides<T extends { components: { schemas: Record<string, unknown> } }>(
31
+ spec: T,
32
+ ): T {
33
+ const copy = structuredClone(spec)
34
+ for (const override of CONTRACT_OVERRIDES) {
35
+ const schema = copy.components.schemas[override.schema] as Record<string, unknown> | undefined
36
+ if (!schema) throw new Error(`The contract has no schema ${override.schema} to override`)
37
+ override.apply(schema)
38
+ }
39
+ return copy
40
+ }
package/src/spec.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /** Loads and indexes the vendored Umbraco Management API OpenAPI contract. */
2
2
  import { readFileSync } from 'node:fs'
3
3
  import { join } from 'node:path'
4
+ import { applyOverrides } from './overrides.ts'
4
5
 
5
6
  export const SPEC_PATH = join(import.meta.dir, '../OpenApi.json')
6
7
 
@@ -40,12 +41,25 @@ export interface OpenApiSpec {
40
41
  }
41
42
 
42
43
  let cached: OpenApiSpec | undefined
44
+ let served: string | undefined
43
45
 
46
+ /** Umbraco's contract exactly as vendored, before crombolo's deliberate differences. */
47
+ export function loadUmbracoSpec(): OpenApiSpec {
48
+ return JSON.parse(readFileSync(SPEC_PATH, 'utf8')) as OpenApiSpec
49
+ }
50
+
51
+ /** The contract crombolo answers to: Umbraco's, with `CONTRACT_OVERRIDES` applied. */
44
52
  export function loadSpec(): OpenApiSpec {
45
- if (!cached) cached = JSON.parse(readFileSync(SPEC_PATH, 'utf8')) as OpenApiSpec
53
+ if (!cached) cached = applyOverrides(loadUmbracoSpec())
46
54
  return cached
47
55
  }
48
56
 
57
+ /** The contract as `GET …/openapi.json` serves it. */
58
+ export function servedContract(): string {
59
+ if (!served) served = JSON.stringify(loadSpec())
60
+ return served
61
+ }
62
+
49
63
  let deliveryCached: OpenApiSpec | undefined
50
64
 
51
65
  export function loadDeliverySpec(): OpenApiSpec {
@@ -55,7 +69,7 @@ export function loadDeliverySpec(): OpenApiSpec {
55
69
  }
56
70
 
57
71
  export async function loadSpecAsync(): Promise<OpenApiSpec> {
58
- if (!cached) cached = (await Bun.file(SPEC_PATH).json()) as OpenApiSpec
72
+ if (!cached) cached = applyOverrides((await Bun.file(SPEC_PATH).json()) as OpenApiSpec)
59
73
  return cached
60
74
  }
61
75