@hey-api/shared 0.1.1 → 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.
package/dist/index.mjs CHANGED
@@ -1782,7 +1782,7 @@ function pathToJsonPointer(path$1) {
1782
1782
  return "#" + (segments ? `/${segments}` : "");
1783
1783
  }
1784
1784
  /**
1785
- * Checks if a $ref points to a top-level component (not a deep path reference).
1785
+ * Checks if a $ref or path points to a top-level component (not a deep path reference).
1786
1786
  *
1787
1787
  * Top-level component references:
1788
1788
  * - OpenAPI 3.x: #/components/{type}/{name} (3 segments)
@@ -1791,11 +1791,11 @@ function pathToJsonPointer(path$1) {
1791
1791
  * Deep path references (4+ segments for 3.x, 3+ for 2.0) should be inlined
1792
1792
  * because they don't have corresponding registered symbols.
1793
1793
  *
1794
- * @param $ref - The $ref string to check
1794
+ * @param refOrPath - The $ref string or path array to check
1795
1795
  * @returns true if the ref points to a top-level component, false otherwise
1796
1796
  */
1797
- function isTopLevelComponentRef($ref) {
1798
- const path$1 = jsonPointerToPath($ref);
1797
+ function isTopLevelComponent(refOrPath) {
1798
+ const path$1 = refOrPath instanceof Array ? refOrPath : jsonPointerToPath(refOrPath);
1799
1799
  if (path$1[0] === "components") return path$1.length === 3;
1800
1800
  if (path$1[0] === "definitions") return path$1.length === 2;
1801
1801
  return false;
@@ -2038,14 +2038,14 @@ var PluginInstance = class {
2038
2038
  symbol(name, symbol) {
2039
2039
  const symbolIn = {
2040
2040
  ...symbol,
2041
- exportFrom: symbol?.exportFrom ?? (!symbol?.external && this.context.config.output.indexFile && this.config.exportFromIndex ? ["index"] : void 0),
2042
- getFilePath: symbol?.getFilePath ?? this.getSymbolFilePath.bind(this),
2043
2041
  meta: {
2044
2042
  pluginName: path.isAbsolute(this.name) ? "custom" : this.name,
2045
2043
  ...symbol?.meta
2046
2044
  },
2047
2045
  name
2048
2046
  };
2047
+ if (symbolIn.getExportFromFilePath === void 0) symbolIn.getExportFromFilePath = this.getSymbolExportFromFilePath.bind(this);
2048
+ if (symbolIn.getFilePath === void 0) symbolIn.getFilePath = this.getSymbolFilePath.bind(this);
2049
2049
  for (const hook of this.eventHooks["symbol:register:before"]) hook({
2050
2050
  plugin: this,
2051
2051
  symbol: symbolIn
@@ -2100,6 +2100,22 @@ var PluginInstance = class {
2100
2100
  pluginName: this.name
2101
2101
  });
2102
2102
  }
2103
+ getSymbolExportFromFilePath(symbol) {
2104
+ const hooks = [this.config["~hooks"]?.symbols, this.context.config.parser.hooks.symbols];
2105
+ for (const hook of hooks) {
2106
+ const result = hook?.getExportFromFilePath?.(symbol);
2107
+ if (result !== void 0) return result;
2108
+ }
2109
+ const entryFile = this.context.config.output.indexFile ?? this.context.config.output.entryFile;
2110
+ if (symbol.external || !entryFile) return;
2111
+ const includeInEntry = this.config.exportFromIndex ?? this.config.includeInEntry;
2112
+ if (typeof includeInEntry === "boolean" && !includeInEntry || typeof includeInEntry === "function" && !includeInEntry(symbol)) return;
2113
+ const language = symbol.node?.language;
2114
+ if (!language) return;
2115
+ const moduleEntryName = this.gen.moduleEntryNames[language];
2116
+ if (!moduleEntryName) return;
2117
+ return [moduleEntryName];
2118
+ }
2103
2119
  getSymbolFilePath(symbol) {
2104
2120
  const hooks = [this.config["~hooks"]?.symbols, this.context.config.parser.hooks.symbols];
2105
2121
  for (const hook of hooks) {
@@ -3564,6 +3580,92 @@ const removeOriginalSplitSchemas = ({ logger, originalSchemas, spec, split }) =>
3564
3580
  event.timeEnd();
3565
3581
  };
3566
3582
  /**
3583
+ * Create writable variants of parent schemas that have discriminators
3584
+ * and are referenced by split schemas.
3585
+ */
3586
+ function splitDiscriminatorSchemas({ config, existingNames, schemasPointerNamespace, spec, split }) {
3587
+ const schemasObj = getSchemasObject(spec);
3588
+ if (!schemasObj) return;
3589
+ const parentSchemasToSplit = /* @__PURE__ */ new Map();
3590
+ for (const [name, schema] of Object.entries(split.schemas)) {
3591
+ const pointer = `${schemasPointerNamespace}${name}`;
3592
+ const originalPointer = split.reverseMapping[pointer];
3593
+ if (originalPointer) {
3594
+ const mapping = split.mapping[originalPointer];
3595
+ if (mapping) {
3596
+ const contextVariant = mapping.read === pointer ? "read" : mapping.write === pointer ? "write" : null;
3597
+ if (contextVariant && schema && typeof schema === "object" && "allOf" in schema && schema.allOf instanceof Array) {
3598
+ for (const comp of schema.allOf) if (comp && typeof comp === "object" && "$ref" in comp && typeof comp.$ref === "string") {
3599
+ const refPath = jsonPointerToPath(comp.$ref);
3600
+ const schemaName = refPath[refPath.length - 1];
3601
+ if (typeof schemaName === "string" && schemaName in schemasObj) {
3602
+ const resolvedSchema = schemasObj[schemaName];
3603
+ if (resolvedSchema && typeof resolvedSchema === "object" && "discriminator" in resolvedSchema && resolvedSchema.discriminator && typeof resolvedSchema.discriminator === "object" && "mapping" in resolvedSchema.discriminator && resolvedSchema.discriminator.mapping && typeof resolvedSchema.discriminator.mapping === "object") {
3604
+ if (!parentSchemasToSplit.has(comp.$ref)) parentSchemasToSplit.set(comp.$ref, /* @__PURE__ */ new Set());
3605
+ parentSchemasToSplit.get(comp.$ref).add(contextVariant);
3606
+ }
3607
+ }
3608
+ }
3609
+ }
3610
+ }
3611
+ }
3612
+ }
3613
+ const parentSchemaVariants = /* @__PURE__ */ new Map();
3614
+ for (const [parentRef, contexts] of parentSchemasToSplit) {
3615
+ const refPath = jsonPointerToPath(parentRef);
3616
+ const parentName = refPath[refPath.length - 1];
3617
+ if (typeof parentName !== "string" || !(parentName in schemasObj)) continue;
3618
+ const parentSchema = schemasObj[parentName];
3619
+ if (!parentSchema || typeof parentSchema !== "object") continue;
3620
+ const variants = {};
3621
+ for (const context of contexts) {
3622
+ const variantSchema = deepClone(parentSchema);
3623
+ if ("discriminator" in variantSchema && variantSchema.discriminator && typeof variantSchema.discriminator === "object" && "mapping" in variantSchema.discriminator && variantSchema.discriminator.mapping && typeof variantSchema.discriminator.mapping === "object") {
3624
+ const mapping = variantSchema.discriminator.mapping;
3625
+ const updatedMapping = {};
3626
+ for (const [discriminatorValue, originalRef] of Object.entries(mapping)) {
3627
+ const map = split.mapping[originalRef];
3628
+ if (map) if (context === "read" && map.read) updatedMapping[discriminatorValue] = map.read;
3629
+ else if (context === "write" && map.write) updatedMapping[discriminatorValue] = map.write;
3630
+ else updatedMapping[discriminatorValue] = originalRef;
3631
+ else updatedMapping[discriminatorValue] = originalRef;
3632
+ }
3633
+ variantSchema.discriminator.mapping = updatedMapping;
3634
+ }
3635
+ if (context === "write") {
3636
+ const writeName = getUniqueComponentName({
3637
+ base: applyNaming(parentName, config.requests),
3638
+ components: existingNames
3639
+ });
3640
+ existingNames.add(writeName);
3641
+ split.schemas[writeName] = variantSchema;
3642
+ variants.write = `${schemasPointerNamespace}${writeName}`;
3643
+ }
3644
+ }
3645
+ parentSchemaVariants.set(parentRef, variants);
3646
+ }
3647
+ for (const [name, schema] of Object.entries(split.schemas)) {
3648
+ const pointer = `${schemasPointerNamespace}${name}`;
3649
+ const originalPointer = split.reverseMapping[pointer];
3650
+ if (!originalPointer) continue;
3651
+ const mapping = split.mapping[originalPointer];
3652
+ if (!mapping) continue;
3653
+ const contextVariant = mapping.read === pointer ? "read" : mapping.write === pointer ? "write" : null;
3654
+ if (contextVariant && schema && typeof schema === "object") {
3655
+ if ("allOf" in schema && schema.allOf instanceof Array) for (let i = 0; i < schema.allOf.length; i++) {
3656
+ const comp = schema.allOf[i];
3657
+ if (comp && typeof comp === "object" && "$ref" in comp && typeof comp.$ref === "string") {
3658
+ const variants = parentSchemaVariants.get(comp.$ref);
3659
+ if (variants) {
3660
+ if (contextVariant === "write" && variants.write) comp.$ref = variants.write;
3661
+ else if (contextVariant === "read" && variants.read) comp.$ref = variants.read;
3662
+ }
3663
+ }
3664
+ }
3665
+ }
3666
+ }
3667
+ }
3668
+ /**
3567
3669
  * Splits schemas with both 'read' and 'write' scopes into read/write variants.
3568
3670
  * Returns the new schemas and a mapping from original pointer to new variant pointers.
3569
3671
  *
@@ -3634,6 +3736,13 @@ const splitSchemas = ({ config, graph: graph$1, logger, spec }) => {
3634
3736
  split.reverseMapping[readPointer] = pointer;
3635
3737
  split.reverseMapping[writePointer] = pointer;
3636
3738
  }
3739
+ splitDiscriminatorSchemas({
3740
+ config,
3741
+ existingNames,
3742
+ schemasPointerNamespace,
3743
+ spec,
3744
+ split
3745
+ });
3637
3746
  event.timeEnd();
3638
3747
  return split;
3639
3748
  };
@@ -3774,6 +3883,26 @@ const updateRefsInSpec = ({ logger, spec, split }) => {
3774
3883
  else if (nextContext === "write" && map.write) node[key] = map.write;
3775
3884
  else if (!nextContext && map.read) node[key] = map.read;
3776
3885
  }
3886
+ } else if (key === "discriminator" && typeof value === "object" && value !== null) {
3887
+ if ("mapping" in value && value.mapping && typeof value.mapping === "object") {
3888
+ const updatedMapping = {};
3889
+ for (const [discriminatorValue, originalRef] of Object.entries(value.mapping)) {
3890
+ const map = split.mapping[originalRef];
3891
+ if (map) if (nextContext === "read" && map.read) updatedMapping[discriminatorValue] = map.read;
3892
+ else if (nextContext === "write" && map.write) updatedMapping[discriminatorValue] = map.write;
3893
+ else updatedMapping[discriminatorValue] = originalRef;
3894
+ else updatedMapping[discriminatorValue] = originalRef;
3895
+ }
3896
+ value.mapping = updatedMapping;
3897
+ }
3898
+ walk$1({
3899
+ context: nextContext,
3900
+ currentPointer: nextPointer,
3901
+ inSchema,
3902
+ node: value,
3903
+ path: [...path$1, key],
3904
+ visited
3905
+ });
3777
3906
  } else walk$1({
3778
3907
  context: nextContext,
3779
3908
  currentPointer: nextPointer,
@@ -4235,7 +4364,7 @@ const parseEnum$2 = ({ context, schema, state }) => {
4235
4364
  };
4236
4365
  const parseRef$2 = ({ context, schema, state }) => {
4237
4366
  const irSchema = {};
4238
- if (!isTopLevelComponentRef(schema.$ref)) {
4367
+ if (!isTopLevelComponent(schema.$ref)) {
4239
4368
  if (!state.circularReferenceTracker.has(schema.$ref)) {
4240
4369
  const refSchema = context.resolveRef(schema.$ref);
4241
4370
  const originalRef = state.$ref;
@@ -5561,7 +5690,7 @@ const parseOneOf$1 = ({ context, schema, state }) => {
5561
5690
  return irSchema;
5562
5691
  };
5563
5692
  const parseRef$1 = ({ context, schema, state }) => {
5564
- if (!isTopLevelComponentRef(schema.$ref)) {
5693
+ if (!isTopLevelComponent(schema.$ref)) {
5565
5694
  if (!state.circularReferenceTracker.has(schema.$ref)) {
5566
5695
  const refSchema = context.resolveRef(schema.$ref);
5567
5696
  const originalRef = state.$ref;
@@ -6909,7 +7038,7 @@ const parseOneOf = ({ context, schema, state }) => {
6909
7038
  return irSchema;
6910
7039
  };
6911
7040
  const parseRef = ({ context, schema, state }) => {
6912
- if (!isTopLevelComponentRef(schema.$ref)) {
7041
+ if (!isTopLevelComponent(schema.$ref)) {
6913
7042
  if (!state.circularReferenceTracker.has(schema.$ref)) {
6914
7043
  const refSchema = context.resolveRef(schema.$ref);
6915
7044
  const originalRef = state.$ref;
@@ -8015,6 +8144,43 @@ function patchOpenApiSpec({ patchOptions, spec: _spec }) {
8015
8144
  }
8016
8145
  }
8017
8146
 
8147
+ //#endregion
8148
+ //#region src/plugins/schema-processor.ts
8149
+ function createSchemaProcessor() {
8150
+ const emitted = /* @__PURE__ */ new Set();
8151
+ let contextTags;
8152
+ let contextAnchor;
8153
+ return {
8154
+ get context() {
8155
+ return {
8156
+ anchor: contextAnchor,
8157
+ tags: contextTags
8158
+ };
8159
+ },
8160
+ hasEmitted(path$1) {
8161
+ return emitted.has(pathToJsonPointer(path$1));
8162
+ },
8163
+ markEmitted(path$1) {
8164
+ const pointer = pathToJsonPointer(path$1);
8165
+ if (emitted.has(pointer)) return false;
8166
+ emitted.add(pointer);
8167
+ return true;
8168
+ },
8169
+ withContext(ctx, fn) {
8170
+ const prevTags = contextTags;
8171
+ const prevAnchor = contextAnchor;
8172
+ contextTags = ctx.tags;
8173
+ contextAnchor = ctx.anchor;
8174
+ try {
8175
+ return fn();
8176
+ } finally {
8177
+ contextTags = prevTags;
8178
+ contextAnchor = prevAnchor;
8179
+ }
8180
+ }
8181
+ };
8182
+ }
8183
+
8018
8184
  //#endregion
8019
8185
  //#region src/plugins/shared/utils/config.ts
8020
8186
  const definePluginConfig = (defaultConfig) => (userConfig) => ({
@@ -8052,5 +8218,109 @@ const utils = {
8052
8218
  };
8053
8219
 
8054
8220
  //#endregion
8055
- export { ConfigError, ConfigValidationError, Context, HeyApiError, IntentContext, JobError, MinHeap, OperationPath, OperationStrategy, PluginInstance, applyNaming, buildGraph, checkNodeVersion, compileInputPath, createOperationKey, debugTools, deduplicateSchema, defaultPaginationKeywords, definePluginConfig, dependencyFactory, encodeJsonPointerSegment, ensureDirSync, escapeComment, findPackageJson, findTsConfigPath, getInput, getLogs, getParser, getSpec, hasOperationDataRequired, hasParameterGroupObjectRequired, hasParametersObjectRequired, heyApiRegistryBaseUrl, inputToApiRegistry, isTopLevelComponentRef, jsonPointerToPath, loadPackageJson, loadTsConfig, logCrashReport, logInputPaths, mappers, normalizeJsonPointer, openGitHubIssueWithCrashReport, operationPagination, operationResponsesMap, parameterWithPagination, parseOpenApiSpec, parseUrl, parseV2_0_X, parseV3_0_X, parseV3_1_X, patchOpenApiSpec, pathToJsonPointer, postprocessOutput, printCliIntro, printCrashReport, refToName, resolveNaming, resolveRef, resolveSource, satisfies, shouldReportCrash, statusCodeToGroup, toCase, utils, valueToObject };
8221
+ //#region src/utils/path.ts
8222
+ /**
8223
+ * After these structural segments, the next segment has a known role.
8224
+ * This is what makes a property literally named "properties" safe —
8225
+ * it occupies the name position, never the structural position.
8226
+ */
8227
+ const STRUCTURAL_ROLE = {
8228
+ items: "index",
8229
+ patternProperties: "name",
8230
+ properties: "name"
8231
+ };
8232
+ /**
8233
+ * These structural segments have no following name/index —
8234
+ * they are the terminal structural node. Append a suffix
8235
+ * to disambiguate from the parent.
8236
+ */
8237
+ const STRUCTURAL_SUFFIX = { additionalProperties: "Value" };
8238
+ /**
8239
+ * Root context configuration.
8240
+ */
8241
+ const ROOT_CONTEXT = {
8242
+ components: {
8243
+ names: 1,
8244
+ skip: 2
8245
+ },
8246
+ definitions: {
8247
+ names: 1,
8248
+ skip: 1
8249
+ },
8250
+ paths: {
8251
+ names: 2,
8252
+ skip: 1
8253
+ },
8254
+ webhooks: {
8255
+ names: 2,
8256
+ skip: 1
8257
+ }
8258
+ };
8259
+ /**
8260
+ * Sanitizes a path segment for use in a derived name.
8261
+ *
8262
+ * Handles API path segments like `/api/v1/users/{id}` → `ApiV1UsersId`.
8263
+ */
8264
+ function sanitizeSegment(segment) {
8265
+ const str = String(segment);
8266
+ if (str.startsWith("/")) return str.split("/").filter(Boolean).map((part) => {
8267
+ const clean = part.replace(/[{}]/g, "");
8268
+ return clean.charAt(0).toUpperCase() + clean.slice(1);
8269
+ }).join("");
8270
+ return str;
8271
+ }
8272
+ /**
8273
+ * Derives a composite name from a path.
8274
+ *
8275
+ * Examples:
8276
+ * .../User → 'User'
8277
+ * .../User/properties/address → 'UserAddress'
8278
+ * .../User/properties/properties → 'UserProperties'
8279
+ * .../User/properties/address/properties/city → 'UserAddressCity'
8280
+ * .../Pet/additionalProperties → 'PetValue'
8281
+ * .../Order/properties/items/items/0 → 'OrderItems'
8282
+ * paths//event/get/properties/query → 'EventGetQuery'
8283
+ *
8284
+ * With anchor:
8285
+ * paths//event/get/properties/query, { anchor: 'event.subscribe' }
8286
+ * → 'event.subscribe-Query'
8287
+ */
8288
+ function pathToName(path$1, options) {
8289
+ const names = [];
8290
+ let index = 0;
8291
+ const rootContext = ROOT_CONTEXT[path$1[0]];
8292
+ if (rootContext) {
8293
+ index = rootContext.skip;
8294
+ if (options?.anchor) {
8295
+ names.push(options.anchor);
8296
+ index += rootContext.names;
8297
+ } else for (let n = 0; n < rootContext.names && index < path$1.length; n++) {
8298
+ names.push(sanitizeSegment(path$1[index]));
8299
+ index++;
8300
+ }
8301
+ } else if (options?.anchor) {
8302
+ names.push(options.anchor);
8303
+ index++;
8304
+ } else if (index < path$1.length) {
8305
+ names.push(sanitizeSegment(path$1[index]));
8306
+ index++;
8307
+ }
8308
+ while (index < path$1.length) {
8309
+ const segment = String(path$1[index]);
8310
+ const role = STRUCTURAL_ROLE[segment];
8311
+ if (role === "name") {
8312
+ index++;
8313
+ if (index < path$1.length) names.push(sanitizeSegment(path$1[index]));
8314
+ } else if (role === "index") {
8315
+ index++;
8316
+ if (index < path$1.length && typeof path$1[index] === "number") index++;
8317
+ continue;
8318
+ } else if (STRUCTURAL_SUFFIX[segment]) names.push(STRUCTURAL_SUFFIX[segment]);
8319
+ index++;
8320
+ }
8321
+ return decodeURI(names.join("-"));
8322
+ }
8323
+
8324
+ //#endregion
8325
+ export { ConfigError, ConfigValidationError, Context, HeyApiError, IntentContext, JobError, MinHeap, OperationPath, OperationStrategy, PluginInstance, addItemsToSchema, applyNaming, buildGraph, checkNodeVersion, compileInputPath, createOperationKey, createSchemaProcessor, debugTools, deduplicateSchema, defaultPaginationKeywords, definePluginConfig, dependencyFactory, encodeJsonPointerSegment, ensureDirSync, escapeComment, findPackageJson, findTsConfigPath, getInput, getLogs, getParser, getSpec, hasOperationDataRequired, hasParameterGroupObjectRequired, hasParametersObjectRequired, heyApiRegistryBaseUrl, inputToApiRegistry, isTopLevelComponent, jsonPointerToPath, loadPackageJson, loadTsConfig, logCrashReport, logInputPaths, mappers, normalizeJsonPointer, openGitHubIssueWithCrashReport, operationPagination, operationResponsesMap, parameterWithPagination, parseOpenApiSpec, parseUrl, parseV2_0_X, parseV3_0_X, parseV3_1_X, patchOpenApiSpec, pathToJsonPointer, pathToName, postprocessOutput, printCliIntro, printCrashReport, refToName, resolveNaming, resolveRef, resolveSource, satisfies, shouldReportCrash, statusCodeToGroup, toCase, utils, valueToObject };
8056
8326
  //# sourceMappingURL=index.mjs.map