@postman/sdk-config 0.1.0 → 0.2.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.
Files changed (37) hide show
  1. package/README.md +139 -124
  2. package/dist/index.cjs +301 -149
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +3 -3
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.js +292 -150
  7. package/dist/index.js.map +1 -1
  8. package/dist/sdk-config/index.cjs +290 -113
  9. package/dist/sdk-config/index.cjs.map +1 -1
  10. package/dist/sdk-config/index.d.cts +2 -2
  11. package/dist/sdk-config/index.d.ts +2 -2
  12. package/dist/sdk-config/index.js +284 -114
  13. package/dist/sdk-config/index.js.map +1 -1
  14. package/dist/sdk-config/v1/index.cjs +290 -113
  15. package/dist/sdk-config/v1/index.cjs.map +1 -1
  16. package/dist/sdk-config/v1/index.d.cts +6903 -289
  17. package/dist/sdk-config/v1/index.d.ts +6903 -289
  18. package/dist/sdk-config/v1/index.js +284 -114
  19. package/dist/sdk-config/v1/index.js.map +1 -1
  20. package/dist/sdk-config-ir/index.cjs +71 -31
  21. package/dist/sdk-config-ir/index.cjs.map +1 -1
  22. package/dist/sdk-config-ir/index.d.cts +2 -2
  23. package/dist/sdk-config-ir/index.d.ts +2 -2
  24. package/dist/sdk-config-ir/index.js +69 -32
  25. package/dist/sdk-config-ir/index.js.map +1 -1
  26. package/dist/sdk-config-ir/v1/index.cjs +71 -31
  27. package/dist/sdk-config-ir/v1/index.cjs.map +1 -1
  28. package/dist/sdk-config-ir/v1/index.d.cts +130 -43
  29. package/dist/sdk-config-ir/v1/index.d.ts +130 -43
  30. package/dist/sdk-config-ir/v1/index.js +69 -32
  31. package/dist/sdk-config-ir/v1/index.js.map +1 -1
  32. package/dist/{typescript-ByDbin_v.d.cts → typescript-DNqK3T3v.d.cts} +5 -1
  33. package/dist/{typescript-ByDbin_v.d.ts → typescript-DNqK3T3v.d.ts} +5 -1
  34. package/docs/releasing.md +114 -0
  35. package/package.json +2 -2
  36. package/src/sdk-config/v1/README.md +91 -23
  37. package/src/sdk-config-ir/v1/README.md +3 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman/sdk-config",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Shared SDK configuration contracts and transformations for Postman SDK generation",
5
5
  "license": "UNLICENSED",
6
6
  "sideEffects": false,
@@ -48,7 +48,7 @@
48
48
  "url": "git+https://github.com/postman-eng/sdk-config.git"
49
49
  },
50
50
  "publishConfig": {
51
- "access": "restricted"
51
+ "access": "public"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=24"
@@ -4,11 +4,30 @@
4
4
  describes portable generation intent and uses the discriminator `schemaVersion: "sdk-config/v1"`.
5
5
 
6
6
  ```ts
7
- import { parseSdkConfigV1, type SdkConfigV1 } from '@postman/sdk-config/sdk-config/v1';
8
-
9
- const config: SdkConfigV1 = parseSdkConfigV1(untrustedInput);
7
+ import {
8
+ parseSdkConfigV1,
9
+ validateSdkConfigV1,
10
+ type SdkConfigV1,
11
+ type SdkConfigV1Document,
12
+ } from '@postman/sdk-config/sdk-config/v1';
13
+
14
+ // Validate a customer-authored document while preserving omitted properties.
15
+ const document: SdkConfigV1Document = validateSdkConfigV1(untrustedInput);
16
+
17
+ // Resolve domain defaults only when preparing a runtime configuration.
18
+ const resolved: SdkConfigV1 = parseSdkConfigV1(document);
10
19
  ```
11
20
 
21
+ `validateSdkConfigV1` is the correct boundary for persisted files, migration output, and request
22
+ transport. It validates the same schema as `parseSdkConfigV1` but returns the sparse authored
23
+ document, so omitted options remain omitted. `parseSdkConfigV1` is the runtime-resolution boundary:
24
+ it materializes shared domain defaults needed while expanding a target into `SdkConfigIrV1`.
25
+
26
+ This separation is intentional. A migration must not invent `sdkVersion`, `apiVersion`, or other
27
+ default-valued settings that were absent from the legacy Fern configuration. An explicit value—even
28
+ one equal to the current default—must remain distinguishable from omission so future default changes
29
+ do not silently alter customer intent.
30
+
12
31
  ### Convert a Fern generator group
13
32
 
14
33
  The Fern CLI can convert its resolved in-memory generator group without serializing or reparsing
@@ -20,6 +39,9 @@ import { mapFernConfigToSdkConfigV1 } from '@postman/sdk-config/sdk-config/v1';
20
39
 
21
40
  const { sdkConfig, unsupportedFields } = mapFernConfigToSdkConfigV1({
22
41
  apiName: definition.rootApiFile.contents.name,
42
+ source: {
43
+ specs: [{ id: 'payments', type: 'openapi', path: './openapi.yml' }],
44
+ },
23
45
  apiVersion: definition.specVersion,
24
46
  sdkVersion,
25
47
  group: {
@@ -35,28 +57,39 @@ representation are returned in `unsupportedFields`; each issue includes a code,
35
57
  severity, reason, suggested manual action, and the SDK Config destination when one exists.
36
58
  `FernConfigMappingError.issues` uses the same structure for missing information and other fatal
37
59
  cases. Generation must not recover omitted values from legacy configuration. Differing
38
- target-specific values for a shared SDK Config block are rejected because combining them would
39
- change behavior. Optional `api` enrichment may be supplied after Fern's definition or IR has already
40
- been lowered into the public API shape.
60
+ target-specific values are retained as sparse target overrides when the public shape can represent
61
+ them. Optional `api` enrichment may be supplied after Fern's definition or IR has already been
62
+ lowered into the public API shape. Credentials and signing material remain outside SDK Config; the
63
+ mapper reports one target-level diagnostic when a Fern output contains them without logging their
64
+ values.
41
65
 
42
66
  ## Contract boundaries
43
67
 
44
68
  `SdkConfigV1` is distinct from both existing configuration contracts:
45
69
 
46
- - `SdkConfigV1` is customer-facing, may contain multiple one-language targets, and does not select
47
- the API source. A client or sdk-gen-api supplies a collection ID, specification ID, or uploaded
48
- file separately.
70
+ - `SdkConfigV1` is customer-facing, may contain multiple one-language targets, and explicitly names
71
+ the local paths or HTTP(S) URLs of the API specifications it configures. A source may be one spec
72
+ or an ordered multi-spec set, with optional namespaces, overlays, overrides, and API import
73
+ settings.
49
74
  - `SdkConfigIrV1` is the normalized, single-target internal input consumed by sdk-gen-core. A server
50
- expands one `SdkConfigV1` into one IR document per target and supplies source and operational
51
- metadata.
75
+ expands one `SdkConfigV1` into one IR document per target, uploads the declared source files, and
76
+ replaces customer paths with internal `specUrl` values without changing the IR shape.
52
77
  - The legacy Postman configuration (`BuildParameters`) is an older producer-specific input. It is
53
78
  not accepted by `parseSdkConfigV1`; legacy translators normalize it independently.
54
79
 
55
- The public schema deliberately has no `source`, `compatibility`, source-origin, artifact ID, signed
56
- URL, S3 location, credential-resolution, or server-generated publishing control fields. Unknown
57
- fields are rejected at every object boundary. Portable API, client, documentation, package, and
58
- language field schemas come from a neutral shared domain layer to prevent drift; the public and
59
- internal envelopes and their operational extensions remain separate explicit allowlists.
80
+ Each public source contains exactly one customer-owned locator: `path` for a local file or `url` for
81
+ an HTTP(S) resource. A generation client must materialize the selected resource and upload the exact
82
+ bytes for the build; the server replaces the customer locator with an internal signed `specUrl` in
83
+ SDK Config IR. Customer URLs never become generator fetch URLs. Signed URLs, S3 locations, and
84
+ server-generated artifact metadata are not valid public locators. The public schema deliberately has
85
+ no `compatibility`, source-origin, artifact ID, credential-resolution, or server-generated
86
+ publishing control fields. Unknown fields are rejected at every object boundary. Portable API,
87
+ client, documentation, package, source settings, and language field schemas come from a neutral
88
+ shared domain layer to prevent drift; the public and internal envelopes and their operational
89
+ extensions remain separate explicit allowlists.
90
+
91
+ Source `path`, `overlays`, and `overrides` values must be relative paths without parent-directory
92
+ segments. Absolute paths and `..` traversal are rejected at validation time.
60
93
 
61
94
  ## Shape
62
95
 
@@ -65,17 +98,26 @@ internal envelopes and their operational extensions remain separate explicit all
65
98
  "schemaVersion": "sdk-config/v1",
66
99
  "sdkName": "Acme SDK",
67
100
  "sdkVersion": "1.0.0",
101
+ "source": {
102
+ "specs": [{ "id": "payments", "type": "openapi", "url": "https://example.com/openapi.yml" }]
103
+ },
68
104
  "api": {},
69
105
  "client": {},
70
106
  "package": { "description": "Acme API SDK" },
71
107
  "output": { "delivery": "zip" },
72
108
  "docs": {},
73
- "generation": { "includeWatermark": true },
109
+ "generation": {
110
+ "includeWatermark": true,
111
+ "naming": { "smartCasing": true, "smartCasingDigitWordBoundary": false }
112
+ },
74
113
  "targets": [
75
114
  {
76
115
  "language": "typescript",
77
116
  "package": { "packageName": "@acme/sdk" },
78
- "generation": { "packageManager": "pnpm" }
117
+ "generation": {
118
+ "packageManager": "pnpm",
119
+ "namespaceExportName": "AcmeSDK"
120
+ }
79
121
  },
80
122
  {
81
123
  "language": "python",
@@ -92,6 +134,20 @@ Each target has exactly one `language`. A language may occur only once, so both
92
134
  targets and differing targets that conflict on the same language are rejected. `generatorVersion`
93
135
  is an optional target-only exact semantic version.
94
136
 
137
+ Field placement follows ownership rather than whichever Fern generator happens to appear first:
138
+
139
+ - API configuration and API version are global.
140
+ - Client, documentation, and common generation settings are global when identical across every
141
+ target; differing values become sparse target overrides.
142
+ - Package identity, output delivery, publication, repositories, generator version, and
143
+ language-specific generation settings remain on their target.
144
+ - SDK name and version use a global baseline with optional target overrides.
145
+
146
+ The root `output` is therefore optional. It is useful as an explicitly authored baseline, but every
147
+ target must have an effective output either from that baseline or from its own `output` block. Fern
148
+ migration writes output directly on each target because repositories and publication registries are
149
+ properties of the generated language artifact.
150
+
95
151
  ## Supported languages
96
152
 
97
153
  SdkConfig v1 is language-agnostic across every sdk-gen-core target available when this contract was
@@ -99,7 +155,7 @@ defined. Each language has an explicitly allowlisted `generation` shape:
99
155
 
100
156
  | Language | `language` value | Representative target setting |
101
157
  | ---------- | ---------------- | ----------------------------- |
102
- | TypeScript | `typescript` | `packageManager` |
158
+ | TypeScript | `typescript` | `namespaceExportName` |
103
159
  | Python | `python` | `pythonVersion` |
104
160
  | Java | `java` | `includeKotlinSnippets` |
105
161
  | Kotlin | `kotlin` | `asyncStyle` |
@@ -122,14 +178,26 @@ When a server expands the public config into single-target IR documents, it appl
122
178
 
123
179
  | Configuration | Rule |
124
180
  | ----------------------- | --------------------------------------------------------------------------------------------------------------- |
125
- | `api`, `client`, `docs` | Shared unchanged by every target; no target override exists. |
126
- | Root `generation` | Shared generation behavior for every target. |
127
- | Target `generation` | Language-specific settings for that target; it supplements the disjoint shared settings. |
181
+ | `api` | Shared unchanged by every target; no target override exists. |
182
+ | `client`, `docs` | Sparse target properties recursively override root properties; omitted properties inherit. Arrays replace. |
183
+ | Root `generation` | Shared generation behavior inherited by every target. |
184
+ | Target `generation` | Sparse common generation properties override root values; language-specific properties configure that target. |
128
185
  | `sdkName`, `sdkVersion` | A target value wins; otherwise the root value is inherited. |
129
186
  | `package` | Target properties override root properties; omitted properties inherit. Arrays replace rather than concatenate. |
130
- | `output` | A target block replaces the complete root output block; otherwise the root block is inherited. |
187
+ | `output` | An optional root baseline is inherited; a target block replaces it completely. Every target must resolve one. |
131
188
  | `apiVersion` | Shared unchanged by every target. |
132
189
 
190
+ Target `client`, `docs`, and common `generation` blocks are intentionally sparse. This allows one
191
+ multi-language document to retain a shared baseline while expressing a Fern setting that differs
192
+ between generators—for example, a Python-specific client name. Nested objects inherit recursively,
193
+ while an explicitly supplied array replaces the inherited array.
194
+
195
+ An integration may also supply operational values at generation time. Those request values take
196
+ precedence over the persisted SDK Config for fields the integration exposes, such as SDK name,
197
+ SDK version, API version, package settings, generator version, and output intent. The SDK Config
198
+ continues to provide every field that the request does not override. Schema/domain defaults are
199
+ applied only after this merge, when the single-target `SdkConfigIrV1` is parsed.
200
+
133
201
  Package publication requirements are validated after applying package and output precedence. For
134
202
  example, a TypeScript target publishing to npm may inherit a root `packageName`, while a Python
135
203
  target must replace an inherited npm output with a PyPI-compatible output.
@@ -209,6 +209,7 @@ audited on 2026-07-17. Counts guide the initial contract; they are not a permane
209
209
  | generator `inlineFileProperties`, `inline-file-properties` | `client.filePropertyStyle` | 1-3 per language |
210
210
  | `inline_request_params` | `client.requestParameterStyle` | 6 Python |
211
211
  | `customPagerName`, `custom-pager-name`, `custom_pager_name` | `generation.naming.pagerName` | 2-5 per language |
212
+ | `smart-casing`, `smart-casing-digit-word-boundary` | `generation.naming` | Preserved only when explicitly set. |
212
213
  | timeout aliases in seconds or milliseconds | `client.timeoutMs` | Translator converts units. |
213
214
  | `skipResponseValidation`, Python `skip_validation` | `client.responseValidation` | Inverted; 12 TS, 8 Python |
214
215
  | `useDefaultRequestParameterValues` aliases | `client.useDefaultRequestParameterValues` | 1 |
@@ -281,10 +282,10 @@ Only options that remain language-specific stay in `generation.language`.
281
282
 
282
283
  | Language | Fern fields retained in language config | Shared mappings lifted out |
283
284
  | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
284
- | TypeScript | `packageManager` (1), `testFramework` (2), `allowCustomFetcher` (13), branded aliases (1), legacy exports (1), `useBigInt`, and positive `serdeLayer` from `noSerdeLayer` (8) | Namespace, validation, inline types, dependencies, wire tests, streams, WebSockets, pager/client names |
285
+ | TypeScript | `packageManager` (1), `testFramework` (2), `namespaceExportName`, `allowCustomFetcher` (13), branded aliases (1), legacy exports (1), `useBigInt`, and positive `serdeLayer` from `noSerdeLayer` (8) | Namespace, validation, inline types, dependencies, wire tests, streams, WebSockets, pager/client names |
285
286
  | Python | Python/Pydantic versions, client filenames (13), Pydantic compatibility, frozen models (1), required optionals (2), union naming (1), field aliases (3), always-initialize optionals, TypedDict requests (3) | Package/module names, enum form, extra fields, response validation, request style, dependencies, timeout, layout, wire tests, WebSockets |
286
287
  | Java/Kotlin | Package layout (3), local dates (2), optional-nullable collapse (2), Gradle settings (7), and JVM async-client style | Maven identity, namespace, names, forward-compatible enums, inline properties, dependencies, pager, wire tests |
287
- | Go | Legacy complex models, union version (3), legacy client options (1), smart casing, and the fern root-client constructor name | Module/package identity, client name, environments, inline properties, pager, wire tests |
288
+ | Go | Legacy complex models, union version (3), legacy client options (1), and the fern root-client constructor name | Module/package identity, client name, environments, smart casing, inline properties, pager, wire tests |
288
289
  | C# | Optional wrapper, simplified dictionaries (1), explicit namespaces (7), core namespace behavior (1), exception handler (5), and the experimental explicit-nullable-optional switch | NuGet identity, namespace, class/error names, pager, wire tests |
289
290
  | PHP | Property access (2) and generated client interfaces (1) | Composer identity, namespace, client/pager names, request style, dependencies, wire tests |
290
291
  | Ruby | Require paths (2) | Gem identity, client name, dependencies, pager, wire tests |