ts-procedures 10.0.0 → 10.1.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/CHANGELOG.md +55 -0
- package/build/adapters/hono/envelope-parity.test.js +17 -6
- package/build/adapters/hono/envelope-parity.test.js.map +1 -1
- package/build/codegen/constants.js +2 -2
- package/build/codegen/constants.js.map +1 -1
- package/build/exports.d.ts +1 -0
- package/build/exports.js +4 -0
- package/build/exports.js.map +1 -1
- package/build/schema/typebox.d.ts +1 -1
- package/build/schema/typebox.js +20 -2
- package/build/schema/typebox.js.map +1 -1
- package/build/schema/typebox.test.js +10 -0
- package/build/schema/typebox.test.js.map +1 -1
- package/build/server/doc-registry.js +3 -0
- package/build/server/doc-registry.js.map +1 -1
- package/build/server/doc-registry.test.js +31 -0
- package/build/server/doc-registry.test.js.map +1 -1
- package/build/server/index.d.ts +1 -0
- package/build/server/index.js +1 -0
- package/build/server/index.js.map +1 -1
- package/build/server/spec-version.d.ts +24 -0
- package/build/server/spec-version.js +26 -0
- package/build/server/spec-version.js.map +1 -0
- package/build/server/types.d.ts +15 -0
- package/build/version.d.ts +9 -0
- package/build/version.js +11 -0
- package/build/version.js.map +1 -0
- package/docs/doc-envelope-spec-v1.md +296 -0
- package/docs/doc-envelope-v1.schema.json +253 -0
- package/docs/http-integrations.md +27 -1
- package/package.json +1 -1
- package/src/adapters/hono/envelope-parity.test.ts +18 -6
- package/src/codegen/constants.ts +2 -2
- package/src/exports.ts +4 -0
- package/src/schema/typebox.test.ts +12 -0
- package/src/schema/typebox.ts +21 -2
- package/src/server/doc-registry.test.ts +34 -0
- package/src/server/doc-registry.ts +3 -0
- package/src/server/index.ts +1 -0
- package/src/server/spec-version.ts +27 -0
- package/src/server/types.ts +15 -0
- package/src/version.ts +11 -0
|
@@ -9,6 +9,18 @@ describe('typebox schema detection', () => {
|
|
|
9
9
|
expect(isTypeboxSchema(typebox)).toBe(true)
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
+
test('it recognizes a top-level Type.Unsafe schema (~unsafe marker, no ~kind)', () => {
|
|
13
|
+
// typebox >= 1.2.0: Type.Unsafe carries `~unsafe` INSTEAD of `~kind`.
|
|
14
|
+
expect(isTypeboxSchema(Type.Unsafe({ type: 'string' }))).toBe(true)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('it recognizes any future ~-prefixed marker (convention, not a name list)', () => {
|
|
18
|
+
// Detection keys off TypeBox's `~` namespace convention, so a marker that
|
|
19
|
+
// doesn't exist yet is still recognized without a code change.
|
|
20
|
+
const futureMarker = { '~somethingNew': true, type: 'object' }
|
|
21
|
+
expect(isTypeboxSchema(futureMarker)).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
12
24
|
test('it rejects non-TypeBox values', () => {
|
|
13
25
|
expect(isTypeboxSchema({ type: 'object' })).toBe(false)
|
|
14
26
|
expect(isTypeboxSchema('string value')).toBe(false)
|
package/src/schema/typebox.ts
CHANGED
|
@@ -3,10 +3,29 @@ import type { TJSONSchema } from './json-schema.js'
|
|
|
3
3
|
|
|
4
4
|
const TYPEBOX_KIND = Symbol.for('TypeBox.Kind')
|
|
5
5
|
|
|
6
|
+
/** TypeBox v1 prefixes every internal marker key with `~`. */
|
|
7
|
+
const TILDE = '~'.charCodeAt(0)
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* TypeBox v1 namespaces all of its internal markers under a `~` prefix
|
|
11
|
+
* (`~kind`, `~unsafe`, `~optional`, ...) — non-enumerable own properties that
|
|
12
|
+
* stay out of the serialized JSON Schema. Detecting the *convention* rather
|
|
13
|
+
* than specific marker names means new markers (e.g. `~unsafe`, added in
|
|
14
|
+
* typebox 1.2.0 and used by `Type.Unsafe(...)` *instead of* `~kind`) are
|
|
15
|
+
* recognized without a code change. Reflect.ownKeys is required because these
|
|
16
|
+
* markers are non-enumerable.
|
|
17
|
+
*/
|
|
18
|
+
function hasTypeBoxV1Marker(schema: object): boolean {
|
|
19
|
+
for (const key of Reflect.ownKeys(schema)) {
|
|
20
|
+
if (typeof key === 'string' && key.charCodeAt(0) === TILDE) return true
|
|
21
|
+
}
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
/**
|
|
7
26
|
* Built-in adapter for TypeBox (`import { Type } from 'typebox'`).
|
|
8
27
|
*
|
|
9
|
-
* Detection covers both typebox v1 (
|
|
28
|
+
* Detection covers both typebox v1 (any `~`-prefixed marker) and legacy
|
|
10
29
|
* `@sinclair/typebox` 0.3x (`Symbol.for('TypeBox.Kind')`). TypeBox schemas
|
|
11
30
|
* already ARE JSON Schema, so conversion is the identity.
|
|
12
31
|
*/
|
|
@@ -14,7 +33,7 @@ export const typeboxAdapter: SchemaAdapter = {
|
|
|
14
33
|
name: 'typebox',
|
|
15
34
|
detect(schema: unknown): boolean {
|
|
16
35
|
if (typeof schema !== 'object' || schema === null) return false
|
|
17
|
-
return
|
|
36
|
+
return hasTypeBoxV1Marker(schema) || TYPEBOX_KIND in schema
|
|
18
37
|
},
|
|
19
38
|
toJsonSchema(schema: unknown): TJSONSchema {
|
|
20
39
|
return schema as TJSONSchema
|
|
@@ -5,7 +5,9 @@ import type {
|
|
|
5
5
|
THttpStreamProcedureRegistration,
|
|
6
6
|
TProcedureRegistration,
|
|
7
7
|
} from '../core/types.js'
|
|
8
|
+
import pkg from '../../package.json' with { type: 'json' }
|
|
8
9
|
import { DocRegistry } from './doc-registry.js'
|
|
10
|
+
import { DOC_ENVELOPE_SPEC_VERSION, DOC_ENVELOPE_GENERATOR_VERSION } from './spec-version.js'
|
|
9
11
|
import { defineErrorTaxonomy } from './errors/taxonomy.js'
|
|
10
12
|
import { buildRpcRouteDoc } from './docs/rpc-doc.js'
|
|
11
13
|
import { buildHttpStreamRouteDoc } from './docs/http-stream-doc.js'
|
|
@@ -201,6 +203,38 @@ describe('DocRegistry', () => {
|
|
|
201
203
|
})
|
|
202
204
|
})
|
|
203
205
|
|
|
206
|
+
// --------------------------------------------------------------------------
|
|
207
|
+
// toJSON() spec metadata
|
|
208
|
+
// --------------------------------------------------------------------------
|
|
209
|
+
describe('toJSON() spec metadata', () => {
|
|
210
|
+
test('emits specVersion (the constant, a number) and generatorVersion (the package version)', () => {
|
|
211
|
+
const out = new DocRegistry().toJSON()
|
|
212
|
+
expect(out.specVersion).toBe(DOC_ENVELOPE_SPEC_VERSION)
|
|
213
|
+
expect(typeof out.specVersion).toBe('number')
|
|
214
|
+
expect(out.generatorVersion).toBe(pkg.version)
|
|
215
|
+
// the exported constant must track the real package version
|
|
216
|
+
expect(DOC_ENVELOPE_GENERATOR_VERSION).toBe(pkg.version)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
test('metadata is present even on an otherwise-empty envelope', () => {
|
|
220
|
+
const out = new DocRegistry({ includeDefaults: false }).toJSON()
|
|
221
|
+
expect(out).toMatchObject({
|
|
222
|
+
specVersion: DOC_ENVELOPE_SPEC_VERSION,
|
|
223
|
+
generatorVersion: pkg.version,
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('transform receives the metadata on its envelope argument', () => {
|
|
228
|
+
const seen = new DocRegistry().toJSON({
|
|
229
|
+
transform: ({ specVersion, generatorVersion }) => ({ specVersion, generatorVersion }),
|
|
230
|
+
})
|
|
231
|
+
expect(seen).toEqual({
|
|
232
|
+
specVersion: DOC_ENVELOPE_SPEC_VERSION,
|
|
233
|
+
generatorVersion: pkg.version,
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
204
238
|
// --------------------------------------------------------------------------
|
|
205
239
|
// toJSON() filter
|
|
206
240
|
// --------------------------------------------------------------------------
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
taxonomyToErrorDocs,
|
|
14
14
|
type ErrorTaxonomy,
|
|
15
15
|
} from './errors/taxonomy.js'
|
|
16
|
+
import { DOC_ENVELOPE_SPEC_VERSION, DOC_ENVELOPE_GENERATOR_VERSION } from './spec-version.js'
|
|
16
17
|
|
|
17
18
|
export type {
|
|
18
19
|
AnyHttpRouteDoc,
|
|
@@ -112,6 +113,8 @@ export class DocRegistry {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
const envelope: DocEnvelope = {
|
|
116
|
+
specVersion: DOC_ENVELOPE_SPEC_VERSION,
|
|
117
|
+
generatorVersion: DOC_ENVELOPE_GENERATOR_VERSION,
|
|
115
118
|
basePath: this.basePath,
|
|
116
119
|
headers: [...this.headers],
|
|
117
120
|
errors: [...this.errors],
|
package/src/server/index.ts
CHANGED
|
@@ -25,5 +25,6 @@ export { buildStreamRouteDoc } from './docs/stream-doc.js'
|
|
|
25
25
|
export { buildHttpRouteDoc } from './docs/http-doc.js'
|
|
26
26
|
export { buildHttpStreamRouteDoc } from './docs/http-stream-doc.js'
|
|
27
27
|
export { DocRegistry } from './doc-registry.js'
|
|
28
|
+
export { DOC_ENVELOPE_SPEC_VERSION, DOC_ENVELOPE_GENERATOR_VERSION } from './spec-version.js'
|
|
28
29
|
export { writeDocEnvelope } from './doc-envelope.js'
|
|
29
30
|
export type { DocEnvelopeSource } from './doc-envelope.js'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PACKAGE_VERSION } from '../version.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Spec version of the {@link DocEnvelope} wire format.
|
|
5
|
+
*
|
|
6
|
+
* Bump this ONLY when the envelope shape changes in a way downstream parsers
|
|
7
|
+
* must react to — field renames, nesting changes, or semantic changes. Do NOT
|
|
8
|
+
* bump it on ordinary package releases (dependency upgrades, bug fixes, new
|
|
9
|
+
* optional fields that older parsers can safely ignore). It is decoupled from
|
|
10
|
+
* the npm package version on purpose, so consumers get a small monotonic
|
|
11
|
+
* integer to branch on instead of having to interpret semver.
|
|
12
|
+
*
|
|
13
|
+
* Consumer contract: an envelope with no `specVersion` field predates this
|
|
14
|
+
* field and MUST be treated as spec version 1.
|
|
15
|
+
*/
|
|
16
|
+
export const DOC_ENVELOPE_SPEC_VERSION = 1
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The ts-procedures package version that produced an envelope (e.g. `"10.0.0"`).
|
|
20
|
+
* Populates the envelope's `generatorVersion` field.
|
|
21
|
+
*
|
|
22
|
+
* Informational only — for telemetry, debugging, and "which release served
|
|
23
|
+
* this?" questions. Do NOT use it to decide parse compatibility; it churns on
|
|
24
|
+
* every release regardless of whether the envelope format changed. Use
|
|
25
|
+
* {@link DOC_ENVELOPE_SPEC_VERSION} for compatibility decisions.
|
|
26
|
+
*/
|
|
27
|
+
export const DOC_ENVELOPE_GENERATOR_VERSION: string = PACKAGE_VERSION
|
package/src/server/types.ts
CHANGED
|
@@ -250,6 +250,21 @@ export interface DocRegistryOutputOptions<TEnvelope = DocEnvelope> {
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
export interface DocEnvelope {
|
|
253
|
+
/**
|
|
254
|
+
* Wire-format version of this envelope. Every envelope produced by
|
|
255
|
+
* `DocRegistry.toJSON()` / `builder.toDocEnvelope()` carries it. Downstream
|
|
256
|
+
* consumers branch on it to pick a parser; bumped only on envelope format
|
|
257
|
+
* changes, NOT on package releases. Optional on the type so hand-built
|
|
258
|
+
* envelopes and pre-existing documents remain valid — an absent value MUST be
|
|
259
|
+
* treated as spec version 1. See {@link DOC_ENVELOPE_SPEC_VERSION}.
|
|
260
|
+
*/
|
|
261
|
+
specVersion?: number
|
|
262
|
+
/**
|
|
263
|
+
* ts-procedures package version that produced this envelope (e.g. `"10.0.0"`).
|
|
264
|
+
* Informational only — do not use for parse-compatibility decisions (use
|
|
265
|
+
* {@link specVersion} for that). See {@link DOC_ENVELOPE_GENERATOR_VERSION}.
|
|
266
|
+
*/
|
|
267
|
+
generatorVersion?: string
|
|
253
268
|
basePath: string
|
|
254
269
|
headers: HeaderDoc[]
|
|
255
270
|
errors: ErrorDoc[]
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import pkg from '../package.json' with { type: 'json' }
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The ts-procedures package version (e.g. `"10.0.0"`), read from `package.json`.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for "which version is this build?" — consumed by the
|
|
7
|
+
* codegen file header and the doc-envelope `generatorVersion`. Import this
|
|
8
|
+
* rather than re-importing `package.json`, so there's one place the version
|
|
9
|
+
* enters the source tree.
|
|
10
|
+
*/
|
|
11
|
+
export const PACKAGE_VERSION: string = pkg.version
|