@thymian/plugin-sampler 0.3.0-canary.20261007-e6cb8dc → 0.3.0-canary.20261007-cdbe1d3
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/dist/cli/check-result.d.ts +88 -0
- package/dist/cli/check-result.d.ts.map +1 -0
- package/dist/cli/check-result.js +160 -0
- package/dist/cli/check-result.js.map +1 -0
- package/dist/cli/commands/sampler/check.d.ts +32 -4
- package/dist/cli/commands/sampler/check.d.ts.map +1 -1
- package/dist/cli/commands/sampler/check.js +126 -88
- package/dist/cli/commands/sampler/check.js.map +1 -1
- package/dist/cli/commands/sampler/init.d.ts +4 -6
- package/dist/cli/commands/sampler/init.d.ts.map +1 -1
- package/dist/cli/commands/sampler/init.js +33 -49
- package/dist/cli/commands/sampler/init.js.map +1 -1
- package/dist/cli/commands/sampler/show.d.ts +11 -0
- package/dist/cli/commands/sampler/show.d.ts.map +1 -0
- package/dist/cli/commands/sampler/show.js +36 -0
- package/dist/cli/commands/sampler/show.js.map +1 -0
- package/dist/cli/commands/sampler/sync.d.ts +11 -0
- package/dist/cli/commands/sampler/sync.d.ts.map +1 -0
- package/dist/cli/commands/sampler/sync.js +65 -0
- package/dist/cli/commands/sampler/sync.js.map +1 -0
- package/dist/cli/commands/sampler/validate.d.ts +3 -10
- package/dist/cli/commands/sampler/validate.d.ts.map +1 -1
- package/dist/cli/commands/sampler/validate.js +57 -126
- package/dist/cli/commands/sampler/validate.js.map +1 -1
- package/dist/generation/closed-schema.d.ts +22 -0
- package/dist/generation/closed-schema.d.ts.map +1 -0
- package/dist/generation/closed-schema.js +45 -0
- package/dist/generation/closed-schema.js.map +1 -0
- package/dist/generation/content-type-strategies/json.content-type-strategy.d.ts.map +1 -1
- package/dist/generation/content-type-strategies/json.content-type-strategy.js +3 -1
- package/dist/generation/content-type-strategies/json.content-type-strategy.js.map +1 -1
- package/dist/generation/content-type-strategies/xml.content-type-strategy.d.ts.map +1 -1
- package/dist/generation/content-type-strategies/xml.content-type-strategy.js +3 -1
- package/dist/generation/content-type-strategies/xml.content-type-strategy.js.map +1 -1
- package/dist/generation/generate-request-sample.d.ts +12 -0
- package/dist/generation/generate-request-sample.d.ts.map +1 -0
- package/dist/generation/{generate-samples.js → generate-request-sample.js} +11 -4
- package/dist/generation/generate-request-sample.js.map +1 -0
- package/dist/generation/request-generators/default-request-generator.d.ts +14 -0
- package/dist/generation/request-generators/default-request-generator.d.ts.map +1 -1
- package/dist/generation/request-generators/default-request-generator.js +18 -1
- package/dist/generation/request-generators/default-request-generator.js.map +1 -1
- package/dist/generation/types/declaration-set.d.ts +124 -0
- package/dist/generation/types/declaration-set.d.ts.map +1 -0
- package/dist/generation/types/declaration-set.js +389 -0
- package/dist/generation/types/declaration-set.js.map +1 -0
- package/dist/generation/types/example-reflection.d.ts +21 -0
- package/dist/generation/types/example-reflection.d.ts.map +1 -0
- package/dist/generation/types/example-reflection.js +135 -0
- package/dist/generation/types/example-reflection.js.map +1 -0
- package/dist/generation/types/generate-type-surface.d.ts +23 -0
- package/dist/generation/types/generate-type-surface.d.ts.map +1 -0
- package/dist/generation/types/generate-type-surface.js +671 -0
- package/dist/generation/types/generate-type-surface.js.map +1 -0
- package/dist/generation/types/schema-type.d.ts +20 -0
- package/dist/generation/types/schema-type.d.ts.map +1 -0
- package/dist/generation/types/schema-type.js +203 -0
- package/dist/generation/types/schema-type.js.map +1 -0
- package/dist/generation/types/self-check-surface.d.ts +32 -0
- package/dist/generation/types/self-check-surface.d.ts.map +1 -0
- package/dist/generation/types/self-check-surface.js +92 -0
- package/dist/generation/types/self-check-surface.js.map +1 -0
- package/dist/generation/types/type-names.d.ts +135 -0
- package/dist/generation/types/type-names.d.ts.map +1 -0
- package/dist/generation/types/type-names.js +213 -0
- package/dist/generation/types/type-names.js.map +1 -0
- package/dist/generation/types/write-type-surface.d.ts +58 -0
- package/dist/generation/types/write-type-surface.d.ts.map +1 -0
- package/dist/generation/types/write-type-surface.js +119 -0
- package/dist/generation/types/write-type-surface.js.map +1 -0
- package/dist/hooks/create-hook-utils.d.ts +22 -6
- package/dist/hooks/create-hook-utils.d.ts.map +1 -1
- package/dist/hooks/create-hook-utils.js +74 -96
- package/dist/hooks/create-hook-utils.js.map +1 -1
- package/dist/hooks/hook-api.d.ts +49 -0
- package/dist/hooks/hook-api.d.ts.map +1 -0
- package/dist/hooks/hook-api.js +105 -0
- package/dist/hooks/hook-api.js.map +1 -0
- package/dist/hooks/hook-diagnostics.d.ts +45 -0
- package/dist/hooks/hook-diagnostics.d.ts.map +1 -0
- package/dist/hooks/hook-diagnostics.js +80 -0
- package/dist/hooks/hook-diagnostics.js.map +1 -0
- package/dist/hooks/hook-errors.d.ts +33 -0
- package/dist/hooks/hook-errors.d.ts.map +1 -1
- package/dist/hooks/hook-errors.js +46 -0
- package/dist/hooks/hook-errors.js.map +1 -1
- package/dist/hooks/hook-registration.d.ts +108 -0
- package/dist/hooks/hook-registration.d.ts.map +1 -0
- package/dist/hooks/hook-registration.js +69 -0
- package/dist/hooks/hook-registration.js.map +1 -0
- package/dist/hooks/hook-runner.d.ts +111 -11
- package/dist/hooks/hook-runner.d.ts.map +1 -1
- package/dist/hooks/hook-runner.js +309 -142
- package/dist/hooks/hook-runner.js.map +1 -1
- package/dist/hooks/hook-utils-factory.d.ts +19 -0
- package/dist/hooks/hook-utils-factory.d.ts.map +1 -0
- package/dist/hooks/hook-utils-factory.js +2 -0
- package/dist/hooks/hook-utils-factory.js.map +1 -0
- package/dist/hooks/hook-utils.d.ts +80 -12
- package/dist/hooks/hook-utils.d.ts.map +1 -1
- package/dist/hooks/invoke-hook.d.ts +71 -0
- package/dist/hooks/invoke-hook.d.ts.map +1 -0
- package/dist/hooks/invoke-hook.js +161 -0
- package/dist/hooks/invoke-hook.js.map +1 -0
- package/dist/hooks/load-user-hooks.d.ts +96 -0
- package/dist/hooks/load-user-hooks.d.ts.map +1 -0
- package/dist/hooks/load-user-hooks.js +432 -0
- package/dist/hooks/load-user-hooks.js.map +1 -0
- package/dist/hooks/nested-request.d.ts +21 -0
- package/dist/hooks/nested-request.d.ts.map +1 -0
- package/dist/hooks/nested-request.js +24 -0
- package/dist/hooks/nested-request.js.map +1 -0
- package/dist/hooks/overlay-args.d.ts +40 -0
- package/dist/hooks/overlay-args.d.ts.map +1 -0
- package/dist/hooks/overlay-args.js +84 -0
- package/dist/hooks/overlay-args.js.map +1 -0
- package/dist/hooks/resolve-targeting.d.ts +16 -0
- package/dist/hooks/resolve-targeting.d.ts.map +1 -0
- package/dist/hooks/resolve-targeting.js +168 -0
- package/dist/hooks/resolve-targeting.js.map +1 -0
- package/dist/hooks/run-scoped-hooks.d.ts +83 -0
- package/dist/hooks/run-scoped-hooks.d.ts.map +1 -0
- package/dist/hooks/run-scoped-hooks.js +153 -0
- package/dist/hooks/run-scoped-hooks.js.map +1 -0
- package/dist/index.d.ts +42 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -165
- package/dist/index.js.map +1 -1
- package/dist/request-sampler.d.ts +40 -11
- package/dist/request-sampler.d.ts.map +1 -1
- package/dist/request-sampler.js +51 -76
- package/dist/request-sampler.js.map +1 -1
- package/dist/sampler-paths.d.ts +19 -0
- package/dist/sampler-paths.d.ts.map +1 -0
- package/dist/sampler-paths.js +22 -0
- package/dist/sampler-paths.js.map +1 -0
- package/dist/selectors/nearest-paths.d.ts +14 -0
- package/dist/selectors/nearest-paths.d.ts.map +1 -0
- package/dist/selectors/nearest-paths.js +32 -0
- package/dist/selectors/nearest-paths.js.map +1 -0
- package/dist/selectors/path-glob.d.ts +94 -0
- package/dist/selectors/path-glob.d.ts.map +1 -0
- package/dist/selectors/path-glob.js +136 -0
- package/dist/selectors/path-glob.js.map +1 -0
- package/dist/selectors/selector-errors.d.ts +17 -0
- package/dist/selectors/selector-errors.d.ts.map +1 -0
- package/dist/selectors/selector-errors.js +139 -0
- package/dist/selectors/selector-errors.js.map +1 -0
- package/dist/selectors/selector.d.ts +55 -0
- package/dist/selectors/selector.d.ts.map +1 -0
- package/dist/selectors/selector.js +305 -0
- package/dist/selectors/selector.js.map +1 -0
- package/dist/selectors/transaction-catalog.d.ts +96 -0
- package/dist/selectors/transaction-catalog.d.ts.map +1 -0
- package/dist/selectors/transaction-catalog.js +193 -0
- package/dist/selectors/transaction-catalog.js.map +1 -0
- package/dist/selectors/transaction-filter.d.ts +119 -0
- package/dist/selectors/transaction-filter.d.ts.map +1 -0
- package/dist/selectors/transaction-filter.js +191 -0
- package/dist/selectors/transaction-filter.js.map +1 -0
- package/dist/ts-path.d.ts +36 -0
- package/dist/ts-path.d.ts.map +1 -0
- package/dist/ts-path.js +46 -0
- package/dist/ts-path.js.map +1 -0
- package/dist/utils.d.ts +1 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -20
- package/dist/utils.js.map +1 -1
- package/dist/validation/canonicalize.d.ts +17 -0
- package/dist/validation/canonicalize.d.ts.map +1 -0
- package/dist/validation/canonicalize.js +29 -0
- package/dist/validation/canonicalize.js.map +1 -0
- package/dist/validation/shared-compiler-host.d.ts +17 -0
- package/dist/validation/shared-compiler-host.d.ts.map +1 -0
- package/dist/validation/shared-compiler-host.js +58 -0
- package/dist/validation/shared-compiler-host.js.map +1 -0
- package/dist/validation/typecheck-hooks.d.ts +37 -0
- package/dist/validation/typecheck-hooks.d.ts.map +1 -0
- package/dist/validation/typecheck-hooks.js +169 -0
- package/dist/validation/typecheck-hooks.js.map +1 -0
- package/dist/validation/validate-sampler.d.ts +79 -0
- package/dist/validation/validate-sampler.d.ts.map +1 -0
- package/dist/validation/validate-sampler.js +89 -0
- package/dist/validation/validate-sampler.js.map +1 -0
- package/package.json +7 -11
- package/dist/cli/commands/sampler/generate/hook.d.ts +0 -10
- package/dist/cli/commands/sampler/generate/hook.d.ts.map +0 -1
- package/dist/cli/commands/sampler/generate/hook.js +0 -19
- package/dist/cli/commands/sampler/generate/hook.js.map +0 -1
- package/dist/cli/generate-hook.d.ts +0 -4
- package/dist/cli/generate-hook.d.ts.map +0 -1
- package/dist/cli/generate-hook.js +0 -96
- package/dist/cli/generate-hook.js.map +0 -1
- package/dist/cli/templates.d.ts +0 -4
- package/dist/cli/templates.d.ts.map +0 -1
- package/dist/cli/templates.js +0 -28
- package/dist/cli/templates.js.map +0 -1
- package/dist/constants.d.ts +0 -12
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -16
- package/dist/constants.js.map +0 -1
- package/dist/generation/generate-samples-for-thymian-format.d.ts +0 -4
- package/dist/generation/generate-samples-for-thymian-format.d.ts.map +0 -1
- package/dist/generation/generate-samples-for-thymian-format.js +0 -18
- package/dist/generation/generate-samples-for-thymian-format.js.map +0 -1
- package/dist/generation/generate-samples.d.ts +0 -4
- package/dist/generation/generate-samples.d.ts.map +0 -1
- package/dist/generation/generate-samples.js.map +0 -1
- package/dist/hooks/generate-request-types.d.ts +0 -34
- package/dist/hooks/generate-request-types.d.ts.map +0 -1
- package/dist/hooks/generate-request-types.js +0 -458
- package/dist/hooks/generate-request-types.js.map +0 -1
- package/dist/hooks/hook-types.d.ts +0 -7
- package/dist/hooks/hook-types.d.ts.map +0 -1
- package/dist/hooks/hook-types.js +0 -2
- package/dist/hooks/hook-types.js.map +0 -1
- package/dist/hooks/load-hooks-from-samples.d.ts +0 -3
- package/dist/hooks/load-hooks-from-samples.d.ts.map +0 -1
- package/dist/hooks/load-hooks-from-samples.js +0 -22
- package/dist/hooks/load-hooks-from-samples.js.map +0 -1
- package/dist/hooks/ts-config.d.ts +0 -10
- package/dist/hooks/ts-config.d.ts.map +0 -1
- package/dist/hooks/ts-config.js +0 -10
- package/dist/hooks/ts-config.js.map +0 -1
- package/dist/samples-structure/file-request-sample.d.ts +0 -20
- package/dist/samples-structure/file-request-sample.d.ts.map +0 -1
- package/dist/samples-structure/file-request-sample.js +0 -5
- package/dist/samples-structure/file-request-sample.js.map +0 -1
- package/dist/samples-structure/get-path-transaction-id.d.ts +0 -3
- package/dist/samples-structure/get-path-transaction-id.d.ts.map +0 -1
- package/dist/samples-structure/get-path-transaction-id.js +0 -22
- package/dist/samples-structure/get-path-transaction-id.js.map +0 -1
- package/dist/samples-structure/merge-tree.d.ts +0 -5
- package/dist/samples-structure/merge-tree.d.ts.map +0 -1
- package/dist/samples-structure/merge-tree.js +0 -118
- package/dist/samples-structure/merge-tree.js.map +0 -1
- package/dist/samples-structure/read-samples-from-dir.d.ts +0 -13
- package/dist/samples-structure/read-samples-from-dir.d.ts.map +0 -1
- package/dist/samples-structure/read-samples-from-dir.js +0 -205
- package/dist/samples-structure/read-samples-from-dir.js.map +0 -1
- package/dist/samples-structure/samples-from-transactions.d.ts +0 -5
- package/dist/samples-structure/samples-from-transactions.d.ts.map +0 -1
- package/dist/samples-structure/samples-from-transactions.js +0 -120
- package/dist/samples-structure/samples-from-transactions.js.map +0 -1
- package/dist/samples-structure/samples-tree-structure.d.ts +0 -106
- package/dist/samples-structure/samples-tree-structure.d.ts.map +0 -1
- package/dist/samples-structure/samples-tree-structure.js +0 -26
- package/dist/samples-structure/samples-tree-structure.js.map +0 -1
- package/dist/samples-structure/structure-meta-on-disc.d.ts +0 -11
- package/dist/samples-structure/structure-meta-on-disc.d.ts.map +0 -1
- package/dist/samples-structure/structure-meta-on-disc.js +0 -2
- package/dist/samples-structure/structure-meta-on-disc.js.map +0 -1
- package/dist/samples-structure/traverse.d.ts +0 -4
- package/dist/samples-structure/traverse.d.ts.map +0 -1
- package/dist/samples-structure/traverse.js +0 -13
- package/dist/samples-structure/traverse.js.map +0 -1
- package/dist/samples-structure/write-samples-to-dir.d.ts +0 -37
- package/dist/samples-structure/write-samples-to-dir.d.ts.map +0 -1
- package/dist/samples-structure/write-samples-to-dir.js +0 -183
- package/dist/samples-structure/write-samples-to-dir.js.map +0 -1
- package/dist/validation/validate-sampler-output.d.ts +0 -38
- package/dist/validation/validate-sampler-output.d.ts.map +0 -1
- package/dist/validation/validate-sampler-output.js +0 -288
- package/dist/validation/validate-sampler-output.js.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Declaration } from './declaration-set.js';
|
|
2
|
+
export type GeneratedType = {
|
|
3
|
+
/** The declarations this type is made of: its own, plus every named
|
|
4
|
+
* component it references. */
|
|
5
|
+
declarations: Declaration[];
|
|
6
|
+
/** The name of the root declaration, usable wherever a type is expected. */
|
|
7
|
+
type: string;
|
|
8
|
+
};
|
|
9
|
+
/** Whether a media type is one whose body has a JSON shape worth typing. */
|
|
10
|
+
export declare function isJsonMediaType(mediaType: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The TypeScript type for one schema, with its `examples` reflected into
|
|
13
|
+
* property types.
|
|
14
|
+
*
|
|
15
|
+
* A non-JSON media type is `unknown`: the sampler can generate a body for it,
|
|
16
|
+
* but a schema is not what describes it, so pretending to type it would be
|
|
17
|
+
* worse than admitting we do not.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateSchemaType(schema: unknown, mediaType: string, typeName: string): Promise<GeneratedType>;
|
|
20
|
+
//# sourceMappingURL=schema-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-type.d.ts","sourceRoot":"","sources":["../../../src/generation/types/schema-type.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,sBAAsB,CAAC;AAG3E,MAAM,MAAM,aAAa,GAAG;IAC1B;kCAC8B;IAC9B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAmMF,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAI1D;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CA+BxB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { isRecord } from '@thymian/core';
|
|
2
|
+
import { compile } from 'json-schema-to-typescript';
|
|
3
|
+
import { closedObjectsToBoolean } from '../closed-schema.js';
|
|
4
|
+
import { splitDeclarations } from './declaration-set.js';
|
|
5
|
+
import { reflectExamples } from './example-reflection.js';
|
|
6
|
+
const UNKNOWN = { declarations: [], type: 'unknown' };
|
|
7
|
+
/**
|
|
8
|
+
* `$defs` is the 2020-12 spelling of the keyword `definitions` names in
|
|
9
|
+
* draft-07, and `json-schema-to-typescript` only resolves the older one. So
|
|
10
|
+
* both the key **and** every `$ref` pointing into it are rewritten — renaming
|
|
11
|
+
* the key alone leaves the pointers aimed at a token that no longer exists,
|
|
12
|
+
* which fails the whole generation with `Missing $ref pointer`.
|
|
13
|
+
*/
|
|
14
|
+
function defsToDefinitions(input) {
|
|
15
|
+
if (Array.isArray(input)) {
|
|
16
|
+
return input.map(defsToDefinitions);
|
|
17
|
+
}
|
|
18
|
+
if (!isRecord(input)) {
|
|
19
|
+
return input;
|
|
20
|
+
}
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const [key, value] of Object.entries(input)) {
|
|
23
|
+
if (key === '$ref' && typeof value === 'string') {
|
|
24
|
+
out[key] = value.replace(/\/\$defs\//g, '/definitions/');
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
out[key === '$defs' ? 'definitions' : key] = defsToDefinitions(value);
|
|
28
|
+
}
|
|
29
|
+
// A top-level `$ref` beside `definitions` is what a description produces when
|
|
30
|
+
// a body is exactly one named schema, and the emitter cannot compile that
|
|
31
|
+
// shape: wrapping the reference in an `allOf` says the same thing in a shape
|
|
32
|
+
// it can.
|
|
33
|
+
if ('$ref' in out && 'definitions' in out) {
|
|
34
|
+
const ref = out['$ref'];
|
|
35
|
+
delete out['$ref'];
|
|
36
|
+
out['allOf'] = [{ $ref: ref }];
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The keywords a schema names ITSELF with, in the library's own precedence
|
|
42
|
+
* order. All three outrank the name `compile()` is handed, so all three have to
|
|
43
|
+
* be gone before the schema is compiled.
|
|
44
|
+
*
|
|
45
|
+
* The library resolves a declaration name as
|
|
46
|
+
* `options.customName?.(…) || schema.title || schema.$id || keyNameFromDefinition`
|
|
47
|
+
* (`json-schema-to-typescript@15/dist/src/parser.js`), and its normalizer only
|
|
48
|
+
* synthesises the `$id` we rely on `if (!schema.$id && !schema.title …)`. So a
|
|
49
|
+
* schema-level `title` — which any hand-written OpenAPI document is likely to
|
|
50
|
+
* carry — makes the library declare `export interface Astronaut` while the
|
|
51
|
+
* surface still references the name we asked for, and the committed file no
|
|
52
|
+
* longer compiles. `id` is not covered by removing `$id`: the normalizer runs a
|
|
53
|
+
* `Transform id to $id` rule AFTER this pass, so the draft-04 spelling walks
|
|
54
|
+
* back in through the very keyword `$id` was removed to protect.
|
|
55
|
+
*
|
|
56
|
+
* None of the three carries type information. `description` is the keyword the
|
|
57
|
+
* library turns into a JSDoc comment and is deliberately KEPT.
|
|
58
|
+
*
|
|
59
|
+
* Enumerating keywords is not a proof, which is why {@link assertDeclares}
|
|
60
|
+
* turns a keyword this list forgets into a loud failure rather than a surface
|
|
61
|
+
* that silently does not compile.
|
|
62
|
+
*/
|
|
63
|
+
const NAME_KEYWORDS = ['title', '$id', 'id'];
|
|
64
|
+
/**
|
|
65
|
+
* The emitter's own extensions, which let a schema write TypeScript directly.
|
|
66
|
+
*
|
|
67
|
+
* `tsType` is printed VERBATIM in place of whatever the node would have
|
|
68
|
+
* compiled to, and `tsEnumNames` mints a `const enum` declaration of its own.
|
|
69
|
+
* An API description is input — it can be fetched over the network, and it is
|
|
70
|
+
* not reviewed the way source is — so leaving these through means a description
|
|
71
|
+
* decides the contents of a file the project commits and compiles against.
|
|
72
|
+
* `tsType: 'any'` alone silently switches off type checking for that property
|
|
73
|
+
* everywhere a hook touches it, and a `tsEnumNames` declaration is named after
|
|
74
|
+
* the property rather than by {@link NameRegistry}, so it can shadow anything.
|
|
75
|
+
*
|
|
76
|
+
* The sampler uses `tsType` itself, for example reflection. That is why this
|
|
77
|
+
* strip runs BEFORE `reflectExamples`: what a description asked for is gone,
|
|
78
|
+
* and what the generator adds afterwards is its own.
|
|
79
|
+
*/
|
|
80
|
+
const TYPE_DIRECTIVES = ['tsType', 'tsEnumNames'];
|
|
81
|
+
/**
|
|
82
|
+
* Where a schema nests another schema, by the shape of the position.
|
|
83
|
+
*
|
|
84
|
+
* The walk below has to be keyword-aware rather than a blind key filter: `id`
|
|
85
|
+
* is a name keyword in a SCHEMA position and an ordinary property NAME inside
|
|
86
|
+
* `properties`, so filtering every key called `id` deletes the `id` property
|
|
87
|
+
* from every body that has one. That is not hypothetical — it is what the first
|
|
88
|
+
* version of this strip did, and the type surface it produced was clean
|
|
89
|
+
* TypeScript describing the wrong object.
|
|
90
|
+
*/
|
|
91
|
+
const NESTED_SCHEMA = {
|
|
92
|
+
/** Positions holding exactly one schema. */
|
|
93
|
+
single: [
|
|
94
|
+
'additionalItems',
|
|
95
|
+
'additionalProperties',
|
|
96
|
+
'contains',
|
|
97
|
+
'else',
|
|
98
|
+
'if',
|
|
99
|
+
'items',
|
|
100
|
+
'not',
|
|
101
|
+
'propertyNames',
|
|
102
|
+
'then',
|
|
103
|
+
'unevaluatedItems',
|
|
104
|
+
'unevaluatedProperties',
|
|
105
|
+
],
|
|
106
|
+
/** Positions holding an array of schemas. */
|
|
107
|
+
list: ['allOf', 'anyOf', 'oneOf', 'prefixItems'],
|
|
108
|
+
/** Positions holding a name-to-schema map, where the KEY is not a keyword. */
|
|
109
|
+
map: [
|
|
110
|
+
'$defs',
|
|
111
|
+
'definitions',
|
|
112
|
+
'dependentSchemas',
|
|
113
|
+
'patternProperties',
|
|
114
|
+
'properties',
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Strips the given keywords at every schema position — the root, a nested
|
|
119
|
+
* property, an array's element, a composition branch and a named component
|
|
120
|
+
* alike, because the emitter reads them the same way at each of them.
|
|
121
|
+
*/
|
|
122
|
+
function stripKeywords(schema, keywords) {
|
|
123
|
+
if (!isRecord(schema)) {
|
|
124
|
+
return schema;
|
|
125
|
+
}
|
|
126
|
+
const node = { ...schema };
|
|
127
|
+
for (const keyword of keywords) {
|
|
128
|
+
delete node[keyword];
|
|
129
|
+
}
|
|
130
|
+
for (const keyword of NESTED_SCHEMA.single) {
|
|
131
|
+
if (keyword in node) {
|
|
132
|
+
node[keyword] = stripKeywords(node[keyword], keywords);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const keyword of NESTED_SCHEMA.list) {
|
|
136
|
+
const branches = node[keyword];
|
|
137
|
+
if (Array.isArray(branches)) {
|
|
138
|
+
node[keyword] = branches.map((branch) => stripKeywords(branch, keywords));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const keyword of NESTED_SCHEMA.map) {
|
|
142
|
+
const members = node[keyword];
|
|
143
|
+
if (isRecord(members)) {
|
|
144
|
+
node[keyword] = Object.fromEntries(Object.entries(members).map(([name, member]) => [
|
|
145
|
+
name,
|
|
146
|
+
stripKeywords(member, keywords),
|
|
147
|
+
]));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return node;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Fails if the library did not declare under the name it was handed.
|
|
154
|
+
*
|
|
155
|
+
* The surface references `typeName`, so a declaration under any other name is a
|
|
156
|
+
* dangling reference in a committed file — a `tsc` error for the user, in
|
|
157
|
+
* generated code they are told not to edit. Catching it here costs one regex
|
|
158
|
+
* and names the schema that caused it.
|
|
159
|
+
*/
|
|
160
|
+
function assertDeclares(declaration, typeName) {
|
|
161
|
+
const declared = new RegExp(`^export (?:interface|type) ${typeName}\\b`, 'm').test(declaration);
|
|
162
|
+
if (!declared) {
|
|
163
|
+
throw new Error(`The type generator asked for a declaration named ${typeName} and did ` +
|
|
164
|
+
`not get one. A schema keyword is overriding the name; see ` +
|
|
165
|
+
`NAME_KEYWORDS in schema-type.ts.`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Whether a media type is one whose body has a JSON shape worth typing. */
|
|
169
|
+
export function isJsonMediaType(mediaType) {
|
|
170
|
+
const essence = mediaType.split(';', 1)[0]?.trim().toLowerCase() ?? '';
|
|
171
|
+
return essence === 'application/json' || /\+json$/.test(essence);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The TypeScript type for one schema, with its `examples` reflected into
|
|
175
|
+
* property types.
|
|
176
|
+
*
|
|
177
|
+
* A non-JSON media type is `unknown`: the sampler can generate a body for it,
|
|
178
|
+
* but a schema is not what describes it, so pretending to type it would be
|
|
179
|
+
* worse than admitting we do not.
|
|
180
|
+
*/
|
|
181
|
+
export async function generateSchemaType(schema, mediaType, typeName) {
|
|
182
|
+
if (!isJsonMediaType(mediaType)) {
|
|
183
|
+
return UNKNOWN;
|
|
184
|
+
}
|
|
185
|
+
if (schema === undefined) {
|
|
186
|
+
return UNKNOWN;
|
|
187
|
+
}
|
|
188
|
+
// The strip comes first so that the only `tsType` the emitter sees is the one
|
|
189
|
+
// example reflection adds next.
|
|
190
|
+
const stripped = stripKeywords(structuredClone(schema), [
|
|
191
|
+
...NAME_KEYWORDS,
|
|
192
|
+
...TYPE_DIRECTIVES,
|
|
193
|
+
]);
|
|
194
|
+
const declaration = await compile(defsToDefinitions(closedObjectsToBoolean(reflectExamples(stripped))), typeName, {
|
|
195
|
+
bannerComment: '',
|
|
196
|
+
additionalProperties: true,
|
|
197
|
+
style: { semi: false },
|
|
198
|
+
$refOptions: { mutateInputSchema: true },
|
|
199
|
+
});
|
|
200
|
+
assertDeclares(declaration, typeName);
|
|
201
|
+
return { declarations: splitDeclarations(declaration), type: typeName };
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=schema-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-type.js","sourceRoot":"","sources":["../../../src/generation/types/schema-type.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type TypeSurface } from './generate-type-surface.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compile the emitted declaration files **alone**, with `skipLibCheck: false`,
|
|
4
|
+
* and report what `tsc` says about them.
|
|
5
|
+
*
|
|
6
|
+
* Every path that consumes the surface afterwards — the scaffolded tsconfig,
|
|
7
|
+
* `typecheckHooks`, the hook-authoring compile probe — sets `skipLibCheck:
|
|
8
|
+
* true`, because a user's own `@types` tree is not the sampler's business to
|
|
9
|
+
* police. That is exactly the flag that stops TypeScript from reporting an
|
|
10
|
+
* error *inside* a `.d.ts`, so the file the sampler commits — and whose diff
|
|
11
|
+
* is the drift signal every other gate trusts — is the one file none of those
|
|
12
|
+
* paths actually check. This is the check that does.
|
|
13
|
+
*
|
|
14
|
+
* Both files are given as program roots, not just one: `hooksApi` imports
|
|
15
|
+
* `requestTypes`, so a root of `requestTypes` alone never pulls `hooksApi`
|
|
16
|
+
* into the program, and a defect confined to the (mostly static) hooks
|
|
17
|
+
* surface would go unreported.
|
|
18
|
+
*/
|
|
19
|
+
export declare function surfaceDiagnostics(surface: TypeSurface): Promise<string[]>;
|
|
20
|
+
/**
|
|
21
|
+
* The self-check gate: refuse to hand back a surface that does not compile.
|
|
22
|
+
*
|
|
23
|
+
* Called wherever a fresh surface is produced — `init`, `sync`, and
|
|
24
|
+
* `validate`'s scratch surface — so no emitter defect, present or future, can
|
|
25
|
+
* hide behind a user's own compiler settings (`skipLibCheck` among them) and
|
|
26
|
+
* surface later as a confusing error in their hooks. The message and the
|
|
27
|
+
* `GeneratedSurfaceError` name deliberately attribute the fault to the
|
|
28
|
+
* generator: whatever a diagnostic here says, it is never something a user's
|
|
29
|
+
* API description or hooks could have caused.
|
|
30
|
+
*/
|
|
31
|
+
export declare function selfCheckSurface(surface: TypeSurface): Promise<void>;
|
|
32
|
+
//# sourceMappingURL=self-check-surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-check-surface.d.ts","sourceRoot":"","sources":["../../../src/generation/types/self-check-surface.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,EAAE,CAAC,CA2DnB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB1E"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { ThymianBaseError } from '@thymian/core';
|
|
5
|
+
import ts from 'typescript';
|
|
6
|
+
import { tsPathRelativeTo } from '../../ts-path.js';
|
|
7
|
+
import { sharedCompilerHost } from '../../validation/shared-compiler-host.js';
|
|
8
|
+
import { HOOKS_API_FILE, REQUEST_TYPES_FILE, } from './generate-type-surface.js';
|
|
9
|
+
/**
|
|
10
|
+
* Compile the emitted declaration files **alone**, with `skipLibCheck: false`,
|
|
11
|
+
* and report what `tsc` says about them.
|
|
12
|
+
*
|
|
13
|
+
* Every path that consumes the surface afterwards — the scaffolded tsconfig,
|
|
14
|
+
* `typecheckHooks`, the hook-authoring compile probe — sets `skipLibCheck:
|
|
15
|
+
* true`, because a user's own `@types` tree is not the sampler's business to
|
|
16
|
+
* police. That is exactly the flag that stops TypeScript from reporting an
|
|
17
|
+
* error *inside* a `.d.ts`, so the file the sampler commits — and whose diff
|
|
18
|
+
* is the drift signal every other gate trusts — is the one file none of those
|
|
19
|
+
* paths actually check. This is the check that does.
|
|
20
|
+
*
|
|
21
|
+
* Both files are given as program roots, not just one: `hooksApi` imports
|
|
22
|
+
* `requestTypes`, so a root of `requestTypes` alone never pulls `hooksApi`
|
|
23
|
+
* into the program, and a defect confined to the (mostly static) hooks
|
|
24
|
+
* surface would go unreported.
|
|
25
|
+
*/
|
|
26
|
+
export async function surfaceDiagnostics(surface) {
|
|
27
|
+
const root = await mkdtemp(join(tmpdir(), 'thymian-surface-check-'));
|
|
28
|
+
try {
|
|
29
|
+
const generated = join(root, 'generated');
|
|
30
|
+
const requestTypes = join(generated, REQUEST_TYPES_FILE);
|
|
31
|
+
const hooksApi = join(generated, HOOKS_API_FILE);
|
|
32
|
+
await mkdir(generated, { recursive: true });
|
|
33
|
+
await writeFile(requestTypes, surface.requestTypes, 'utf-8');
|
|
34
|
+
await writeFile(hooksApi, surface.hooksApi, 'utf-8');
|
|
35
|
+
const options = {
|
|
36
|
+
strict: true,
|
|
37
|
+
noEmit: true,
|
|
38
|
+
module: ts.ModuleKind.NodeNext,
|
|
39
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
40
|
+
target: ts.ScriptTarget.ES2023,
|
|
41
|
+
// The whole point of this probe: do not skip the files under test.
|
|
42
|
+
skipLibCheck: false,
|
|
43
|
+
baseUrl: root,
|
|
44
|
+
paths: { '@thymian/hooks': [`./generated/${HOOKS_API_FILE}`] },
|
|
45
|
+
lib: ['lib.es2023.d.ts'],
|
|
46
|
+
types: [],
|
|
47
|
+
};
|
|
48
|
+
const program = ts.createProgram([hooksApi, requestTypes], options, sharedCompilerHost(options));
|
|
49
|
+
return ts.getPreEmitDiagnostics(program).flatMap((diagnostic) => {
|
|
50
|
+
const file = diagnostic.file;
|
|
51
|
+
// `generated` is a `node:path` value; TypeScript's own `fileName`s are
|
|
52
|
+
// forward-slashed. `tsPathRelativeTo` normalizes both, which keeps this
|
|
53
|
+
// correct on Windows.
|
|
54
|
+
const relativeFile = file
|
|
55
|
+
? tsPathRelativeTo(file.fileName, generated)
|
|
56
|
+
: undefined;
|
|
57
|
+
if (!file || relativeFile === undefined) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const { line } = file.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
|
|
61
|
+
const where = `${relativeFile}:${line + 1}`;
|
|
62
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, ' ');
|
|
63
|
+
return [`${where} TS${diagnostic.code}: ${message}`];
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await rm(root, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The self-check gate: refuse to hand back a surface that does not compile.
|
|
72
|
+
*
|
|
73
|
+
* Called wherever a fresh surface is produced — `init`, `sync`, and
|
|
74
|
+
* `validate`'s scratch surface — so no emitter defect, present or future, can
|
|
75
|
+
* hide behind a user's own compiler settings (`skipLibCheck` among them) and
|
|
76
|
+
* surface later as a confusing error in their hooks. The message and the
|
|
77
|
+
* `GeneratedSurfaceError` name deliberately attribute the fault to the
|
|
78
|
+
* generator: whatever a diagnostic here says, it is never something a user's
|
|
79
|
+
* API description or hooks could have caused.
|
|
80
|
+
*/
|
|
81
|
+
export async function selfCheckSurface(surface) {
|
|
82
|
+
const diagnostics = await surfaceDiagnostics(surface);
|
|
83
|
+
if (diagnostics.length === 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
throw new ThymianBaseError(`The sampler generated a type surface that does not compile (${diagnostics.length} ${diagnostics.length === 1 ? 'diagnostic' : 'diagnostics'}). This is a defect in the sampler's own generator — not your API description or your hooks — please report it.`, {
|
|
87
|
+
name: 'GeneratedSurfaceError',
|
|
88
|
+
ref: 'https://thymian.dev/references/errors/generated-surface-error/',
|
|
89
|
+
suggestions: diagnostics,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=self-check-surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-check-surface.js","sourceRoot":"","sources":["../../../src/generation/types/self-check-surface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic TypeScript identifiers for the declarations the type surface
|
|
3
|
+
* emits.
|
|
4
|
+
*
|
|
5
|
+
* Every name is a pure function of the transaction's selector plus the schema's
|
|
6
|
+
* role within that transaction. Naming by catalog position instead
|
|
7
|
+
* (`Transaction4Type2`) is stable under a document reorder but not under an
|
|
8
|
+
* *insertion*: a new endpoint that sorts first renumbers every declaration
|
|
9
|
+
* after it. The surface is committed and the diff against it **is** the
|
|
10
|
+
* staleness signal, so a positional name turns adding one endpoint into a
|
|
11
|
+
* whole-file diff — 282 changed lines on the demo description, for one added
|
|
12
|
+
* operation.
|
|
13
|
+
*
|
|
14
|
+
* SANITISATION IS NOT COSMETIC AND THE COLLISION SUFFIX IS NOT DEFENSIVE. A
|
|
15
|
+
* selector legitimately carries characters no identifier can: a method is an
|
|
16
|
+
* RFC 9110 section 5.6.2 token, paths carry `/`, `{`, `}` and percent-encoding,
|
|
17
|
+
* and media types carry `/`, `+`, `;` and `=`. Two distinct selectors therefore
|
|
18
|
+
* collapse onto one stem — methods `A-B` and `A.B` on the same path both
|
|
19
|
+
* sanitise to `AB` — so the suffix is reached by real input.
|
|
20
|
+
*
|
|
21
|
+
* THE NAME WE REFERENCE AND THE NAME THE LIBRARY DECLARES ARE ONE STRING.
|
|
22
|
+
* `json-schema-to-typescript` does not declare under the name it is handed: it
|
|
23
|
+
* declares under `toSafeString(name)`, which re-cases a lowercase letter that
|
|
24
|
+
* follows a digit (`V1beta` becomes `V1Beta`), collapses `_x` into `X` and
|
|
25
|
+
* upper-cases the first letter. A positional name is already a fixed point of
|
|
26
|
+
* that transform; a selector-derived one is not, and a name that changes on the
|
|
27
|
+
* way in is a dangling reference on the way out. {@link safeIdentifier} is the
|
|
28
|
+
* single boundary, and {@link NameRegistry} uniquifies AFTER it — so two
|
|
29
|
+
* candidates that sanitise onto one identifier get a suffix rather than one
|
|
30
|
+
* declaration emitted twice under two names.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The fixed declarations `generate-type-surface.ts` emits around the
|
|
34
|
+
* generated ones. Shared by {@link NameRegistry} (so a generated declaration
|
|
35
|
+
* never shadows one of these) and by `DeclarationSet` (so a description
|
|
36
|
+
* component named after one of these — `Status`, say — is reserved out from
|
|
37
|
+
* under it rather than silently merged into it, which is what let a
|
|
38
|
+
* description declaring a `Status` component reach the committed surface as
|
|
39
|
+
* two conflicting `export type Status` and `export interface Status`
|
|
40
|
+
* declarations, `skipLibCheck` hiding the fault until a user's own hook
|
|
41
|
+
* compile — or, once the self-check gate is on, at generation time with an
|
|
42
|
+
* unattributed `TS2300`).
|
|
43
|
+
*/
|
|
44
|
+
export declare const FIXED_ROOT_NAMES: readonly ["Endpoints", "Method", "Path", "RequestMediaType", "ResponseMediaType", "Responses", "Selector", "Status", "StatusClass", "TransactionResponse", "WireMediaType"];
|
|
45
|
+
/** Which schema of a transaction a declaration was generated for. */
|
|
46
|
+
export type SchemaRole = {
|
|
47
|
+
readonly kind: 'request-body';
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: 'response-body';
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'query-parameter';
|
|
52
|
+
readonly parameter: string;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: 'path-parameter';
|
|
55
|
+
readonly parameter: string;
|
|
56
|
+
} | {
|
|
57
|
+
readonly kind: 'request-header';
|
|
58
|
+
readonly parameter: string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'cookie';
|
|
61
|
+
readonly parameter: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: 'response-header';
|
|
64
|
+
readonly parameter: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Splits on every run of non-alphanumeric characters and upper-cases each
|
|
68
|
+
* remaining segment's first character, so `GET`, `astronauts`, `{id}` and
|
|
69
|
+
* `application/vnd.Example+JSON` become `Get`, `Astronauts`, `Id` and
|
|
70
|
+
* `ApplicationVndExampleJson`.
|
|
71
|
+
*
|
|
72
|
+
* The tail is lower-cased deliberately: a declaration NAME is not required to
|
|
73
|
+
* be byte-exact — only the emitted selector keys and union members are — and
|
|
74
|
+
* folding it keeps `JSON` and `json` from producing two visually
|
|
75
|
+
* indistinguishable identifiers.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pascalSegments(value: string): string;
|
|
78
|
+
/** The role's contribution to a declaration name. */
|
|
79
|
+
export declare function roleSuffix(role: SchemaRole): string;
|
|
80
|
+
/**
|
|
81
|
+
* The identifier a selector plus a role WANTS. Several sites may want the same
|
|
82
|
+
* one; {@link NameRegistry} is what turns wants into names.
|
|
83
|
+
*
|
|
84
|
+
* A method is an RFC 9110 token, so it may be all digits (`123 /x` is a legal
|
|
85
|
+
* selector), and a parameter name may start with one (`2fa`). Neither an
|
|
86
|
+
* identifier nor the library's own transform tolerates a leading digit, so the
|
|
87
|
+
* candidate leaves through {@link safeIdentifier} rather than through a local
|
|
88
|
+
* guard.
|
|
89
|
+
*/
|
|
90
|
+
export declare function candidateName(selector: string, role: SchemaRole): string;
|
|
91
|
+
/**
|
|
92
|
+
* An identifier `json-schema-to-typescript`'s `toSafeString` leaves alone, for
|
|
93
|
+
* any input string.
|
|
94
|
+
*
|
|
95
|
+
* The contract is deliberately weaker than "reproduce `toSafeString`": the
|
|
96
|
+
* library never sees the original, only this function's output, so all that has
|
|
97
|
+
* to hold is that the output is a FIXED POINT of the library's transform. That
|
|
98
|
+
* is four conditions, and each is enforced by one branch below:
|
|
99
|
+
*
|
|
100
|
+
* - every character is in `[A-Za-z0-9_$]`, so nothing is replaced by whitespace;
|
|
101
|
+
* - the first character is never a digit, hence the `_` prefix;
|
|
102
|
+
* - no lowercase letter follows a digit, a `$` or an `_`;
|
|
103
|
+
* - the first character is never lowercase, so `upperFirst` is a no-op.
|
|
104
|
+
*
|
|
105
|
+
* A run of unusable characters is a word boundary rather than a deletion, so
|
|
106
|
+
* `foo bar` and `foo-bar` both become `FooBar` — the same convention
|
|
107
|
+
* {@link pascalSegments} uses, which is why applying both is harmless.
|
|
108
|
+
*/
|
|
109
|
+
export declare function safeIdentifier(value: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* Hands out unique identifiers, remembering what each site was given.
|
|
112
|
+
*
|
|
113
|
+
* The suffix order is a function of the description rather than of traversal
|
|
114
|
+
* order, because the catalog is sorted by selector and each transaction's
|
|
115
|
+
* schemas are visited in a fixed role order. Adding a transaction that collides
|
|
116
|
+
* with an existing stem can renumber the colliding group, and only that group;
|
|
117
|
+
* adding a non-colliding one — the overwhelmingly common case — renames
|
|
118
|
+
* nothing.
|
|
119
|
+
*/
|
|
120
|
+
export declare class NameRegistry {
|
|
121
|
+
private readonly used;
|
|
122
|
+
private readonly byKey;
|
|
123
|
+
/**
|
|
124
|
+
* @param reserved names nothing may be assigned, such as the fixed
|
|
125
|
+
* declarations the surface emits around the generated ones.
|
|
126
|
+
*/
|
|
127
|
+
constructor(reserved?: Iterable<string>);
|
|
128
|
+
/**
|
|
129
|
+
* The name for `selector` in `role`, minted the first time it is asked for
|
|
130
|
+
* and returned unchanged thereafter — so two references to one site cannot
|
|
131
|
+
* drift apart.
|
|
132
|
+
*/
|
|
133
|
+
nameFor(selector: string, role: SchemaRole): string;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=type-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-names.d.ts","sourceRoot":"","sources":["../../../src/generation/types/type-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,6KAYnB,CAAC;AAEX,qEAAqE;AACrE,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASpD;AAcD,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAiBnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAMxE;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiCpD;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IAEnD;;;OAGG;gBACS,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAM;IAI3C;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM;CAsBpD"}
|