@cmflow/atlas 3.4.0-beta.8 → 3.4.0-beta.9

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/README.md CHANGED
@@ -70,6 +70,8 @@ A backend can be either a string or a `BackendSource` created with `defineBacken
70
70
  - a string (for example `"ICC"`) has no field reference; Atlas keeps the current code-analysis and optional inference flow;
71
71
  - a `BackendSource` provides a `resolve` function that returns the backend's known properties. Atlas assigns its `name` as `backend` on every resolved property.
72
72
 
73
+ When a string and a source share the same name, the last declaration wins. This lets a source enrich a list of backend names, for example `[..., Object.values(BackendTypes), quableBackendSource]`.
74
+
73
75
  The resolver returns one of the following shapes. Document-oriented backends have no route; REST backends have no document.
74
76
 
75
77
  ```ts
@@ -159,14 +161,44 @@ export const cmsLegacy = defineBackendSource({
159
161
  `openapiSource` reuses Atlas's OpenAPI parsing utilities: downloading the document, resolving references, traversing operations, and extracting field descriptions. A project therefore only supplies the URL.
160
162
 
161
163
  ```ts
162
- import { defineBackendSource, openapiSource } from "@cmflow/atlas";
164
+ import { constant, defineBackendSource, openapiSource } from "@cmflow/atlas";
163
165
 
164
166
  export const xm = defineBackendSource({
165
167
  name: "XM",
166
- resolve: () => openapiSource({ url: "https://xm.example/openapi.json" })
168
+ resolve: () =>
169
+ openapiSource({
170
+ url: constant<string>("XM_API_URL", "https://xm.example/openapi.json")
171
+ })
167
172
  });
168
173
  ```
169
174
 
175
+ `constant` resolves an environment variable directly from `process.env`, falling back to its second argument. No resolver context is required.
176
+
177
+ ### Environment constants
178
+
179
+ Use `constant` when a backend source needs a configurable value such as a URL or an access token. The value is resolved when the source runs:
180
+
181
+ 1. Atlas uses `process.env[name]` when it is set.
182
+ 2. Otherwise, it returns the supplied default value.
183
+
184
+ ```ts
185
+ import { constant, defineBackendSource, openapiSource } from "@cmflow/atlas";
186
+
187
+ export const xmBackendSource = defineBackendSource({
188
+ name: "XM",
189
+ resolve: () =>
190
+ openapiSource({
191
+ url: constant<string>("XM_API_URL", "https://xm.example/openapi.json")
192
+ })
193
+ });
194
+ ```
195
+
196
+ For example, override the default in CI or locally:
197
+
198
+ ```bash
199
+ XM_API_URL=https://xm.internal/openapi.json atlas backend-sources --backend XM
200
+ ```
201
+
170
202
  This returns entries such as:
171
203
 
172
204
  ```ts
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as isKnownBackendType, c as taskProgressService, d as setUserConfig, i as inferBackendNameFromFile, l as getUserConfig, n as generateBackendTopologyArtifactsInWorkers, o as filterAnalysisFiles, r as resolveModulePath, s as shouldKeepAnalysisFile, u as loadAtlasConfig } from "../routeBackendTopologyService-DkNyCtKt.mjs";
2
+ import { a as isKnownBackendType, c as taskProgressService, d as setUserConfig, i as inferBackendNameFromFile, l as getUserConfig, n as generateBackendTopologyArtifactsInWorkers, o as filterAnalysisFiles, r as resolveModulePath, s as shouldKeepAnalysisFile, u as loadAtlasConfig } from "../routeBackendTopologyService-DElirHSh.mjs";
3
3
  import { Command, InvalidArgumentError, Option } from "commander";
4
4
  import path from "node:path";
5
5
  import { Node, Project, SyntaxKind } from "ts-morph";
@@ -2800,8 +2800,10 @@ var cleanOrphans_default = (program) => void program.command("clean-orphans").op
2800
2800
  //#endregion
2801
2801
  //#region src/commands/backendSources.ts
2802
2802
  async function listBackendSourceMetadata(sources, backend) {
2803
- const selectedSources = backend ? sources.filter((source) => source.name === backend) : sources;
2804
- if (backend && !selectedSources.length) throw new Error(`Unknown backend source: ${backend}`);
2803
+ const resolvableSources = sources.filter((source) => Boolean(source.resolve));
2804
+ const selectedSources = backend ? resolvableSources.filter((source) => source.name === backend) : resolvableSources;
2805
+ if (backend && !selectedSources.length) throw new Error(`Unknown backend source with a resolver: ${backend}`);
2806
+ if (!backend && !selectedSources.length) throw new Error("No backend source with a resolver is configured");
2805
2807
  const propertiesByBackend = await resolveBackendProperties(selectedSources);
2806
2808
  return selectedSources.map((source) => ({
2807
2809
  backend: source.name,
@@ -2811,13 +2813,13 @@ async function listBackendSourceMetadata(sources, backend) {
2811
2813
  var backendSources_default = (program) => void program.command("backend-sources").option("--backend <name>", "Only execute one backend source").description("Execute configured backend sources and display their resolved property metadata").action(async (options) => {
2812
2814
  intro("Atlas backend source metadata");
2813
2815
  try {
2814
- const sources = getUserConfig().analysis.backends;
2816
+ const sources = getUserConfig().analysis.backends.filter((source) => Boolean(source.resolve));
2815
2817
  const selectedBackend = options.backend || await select({
2816
2818
  message: "Which backend source do you want to execute?",
2817
2819
  options: sources.map((source) => ({
2818
2820
  value: source.name,
2819
2821
  label: source.name,
2820
- hint: source.resolve ? "resolver configured" : "no resolver configured"
2822
+ hint: "resolver configured"
2821
2823
  }))
2822
2824
  });
2823
2825
  if (isCancel(selectedBackend)) {
@@ -1 +1 @@
1
- {"version":3,"file":"defineExpressionRule-Dfvzj6n2.mjs","names":[],"sources":["../src/utils/defineExpressionRule.ts"],"sourcesContent":["import type { Expression } from \"ts-morph\";\nimport type { UserConfig } from \"../models/types\";\n\nexport type FieldExtractionRuleResult = {\n backendField?: string;\n transparent?: boolean;\n mapperType?: string;\n apiMapping?: boolean;\n};\n\nexport type FieldExtractionRule = {\n name: string;\n match: (expression: Expression, config: UserConfig) => boolean;\n parse: (expression: Expression, config: UserConfig) => FieldExtractionRuleResult | undefined;\n};\n\nexport function defineExpressionRule(rule: FieldExtractionRule): FieldExtractionRule {\n return rule;\n}\n"],"mappings":"AAgBA,SAAgB,EAAqB,EAAgD,CACnF,OAAO,CACT"}
1
+ {"version":3,"file":"defineExpressionRule-Dfvzj6n2.mjs","names":[],"sources":["../src/utils/defineExpressionRule.ts"],"sourcesContent":["import type { Expression } from \"ts-morph\";\nimport type { UserConfig } from \"../models/types\";\n\nexport type FieldExtractionRuleResult = {\n backendField?: string;\n transparent?: boolean;\n mapperType?: string;\n apiMapping?: boolean;\n};\n\nexport type FieldExtractionRule = {\n name: string;\n /** Higher-priority rules are evaluated first. Defaults to 0. */\n priority?: number;\n match: (expression: Expression, config: UserConfig) => boolean;\n parse: (expression: Expression, config: UserConfig) => FieldExtractionRuleResult | undefined;\n};\n\nexport function defineExpressionRule(rule: FieldExtractionRule): FieldExtractionRule {\n return rule;\n}\n"],"mappings":"AAkBA,SAAgB,EAAqB,EAAgD,CACnF,OAAO,CACT"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineExpressionRule, n as BackendSource, r as UserConfig, t as BackendProperty } from "./types-smD5SZe9.mjs";
1
+ import { a as defineExpressionRule, n as BackendSource, r as UserConfig, t as BackendProperty } from "./types-cYprdLUO.mjs";
2
2
  import { cmsI18nFieldRule } from "./rules/cmsI18nFieldRule.mjs";
3
3
  import { quableI18nFieldRule } from "./rules/quableI18nFieldRule.mjs";
4
4
  //#region src/utils/defineConfig.d.ts
@@ -7,6 +7,15 @@ declare function defineConfig(config: UserConfig): UserConfig;
7
7
  //#region src/utils/defineBackendSource.d.ts
8
8
  declare function defineBackendSource(source: BackendSource): BackendSource;
9
9
  //#endregion
10
+ //#region src/utils/constant.d.ts
11
+ /**
12
+ * Resolves an environment variable, falling back to the supplied default value.
13
+ *
14
+ * This is intended for values declared directly in an Atlas configuration, such
15
+ * as URLs or tokens used by a backend source.
16
+ */
17
+ declare function constant<T extends string = string>(name: string, defaultValue: T): string;
18
+ //#endregion
10
19
  //#region src/services/http/httpClient.d.ts
11
20
  declare class HttpClient {
12
21
  fetch(url: string, init: RequestInit & {
@@ -23,5 +32,5 @@ declare function openapiSource(params: {
23
32
  timeoutMs?: number;
24
33
  }): Promise<BackendProperty[]>;
25
34
  //#endregion
26
- export { type BackendProperty, type BackendSource, type UserConfig, cmsI18nFieldRule, defineBackendSource, defineConfig, defineExpressionRule, httpClient, openapiSource, quableI18nFieldRule };
35
+ export { type BackendProperty, type BackendSource, type UserConfig, cmsI18nFieldRule, constant, defineBackendSource, defineConfig, defineExpressionRule, httpClient, openapiSource, quableI18nFieldRule };
27
36
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./defineExpressionRule-Dfvzj6n2.mjs";import{cmsI18nFieldRule as t}from"./rules/cmsI18nFieldRule.mjs";import{quableI18nFieldRule as n}from"./rules/quableI18nFieldRule.mjs";import{AsyncLocalStorage as r}from"node:async_hooks";import{spinner as i}from"@clack/prompts";function a(e){return e}function o(e){return e}const s=new class{async fetch(e,t){let n={accept:`application/json`,...t?.headers},r=await fetch(e,{...t,headers:n});if(!r.ok)throw Error(`Unable to fetch ${e}: ${r.status} ${r.statusText}`);if(t?.onProgress){let e=r.body?.getReader();if(!e)return r.json();let n=new TextDecoder,i=``;for(;;){let{done:r,value:a}=await e.read();if(r)break;i+=n.decode(a,{stream:!0}),t.onProgress(i)}return i+n.decode()}return r}async get(e,t){return(await this.fetch(e,t)).json()}};function c(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(1)}s`}const l=new class{#e=new r;attach(e,t){return this.#e.run(e,t)}log(e){this.#e.getStore()?.log(e)}report(e){let t=this.#e.getStore();(t?.report||t?.log)?.(e)}createStepProgress(e=e=>({id:e,title:e})){let t=i(),n,r=e=>{if(!n)return;let r=c(Date.now()-n.startedAt);t.stop(`${e||n.completedTitle||`${n.title} completed`} (${r})`),n=void 0},a=e=>{if(n?.id===e.id){t.message(e.detail?`${e.title}: ${e.detail}`:e.title);return}r(),n={id:e.id,title:e.title,completedTitle:e.completedTitle,startedAt:Date.now()},t.start(e.detail?`${e.title}: ${e.detail}`:e.title)},o=r=>this.attach({log:e=>t.message(n?`${n.title}: ${e}`:e),report:t=>a(e(t))},r);return{report(t){a(e(t))},start:a,execute:o,run:async(e,t)=>{a(e),await new Promise(e=>setImmediate(e));let n=await o(t);return r(),n},finish(e){r(e)},fail(e){if(!n)return;let r=c(Date.now()-n.startedAt);t.stop(`${e} (${r})`),n=void 0}}}};async function u(e,t){try{let n=await s.fetch(e,{signal:t?AbortSignal.timeout(t):void 0,onProgress(e){l.log(`Downloading (${(e.length/1048576).toFixed(1)} MB)`)}});return!n||typeof n==`object`?n:(l.log(`Parsing document`),JSON.parse(n))}catch(n){throw n instanceof Error&&(n.name===`AbortError`||n.name===`TimeoutError`)?Error(`OpenAPI download timed out after ${t}ms: ${e}`):Error(`Unable to fetch OpenAPI document from ${e}: ${n instanceof Error?n.message:String(n)}`)}}function d(e,t){if(!e||typeof e!=`object`||typeof e.$ref!=`string`||!e.$ref.startsWith(`#/`))return e;let n=t;for(let t of e.$ref.replace(`#/`,``).split(`/`).map(e=>e.replace(/~1/g,`/`).replace(/~0/g,`~`)))if(n=n?.[t],n===void 0)return e;return n}function f(e){return/^\d+$/.test(e)&&Number(e)>=200&&Number(e)<=299}function p(e,t){if(!e||typeof e!=`object`)return;if(e[`application/json`]?.schema)return d(e[`application/json`].schema,t);let n=Object.values(e).find(e=>e?.schema);return n?d(n.schema,t):void 0}function m(e,t,n=``){let r=d(e,t);if(!r||typeof r!=`object`)return[];if(Array.isArray(r.allOf))return r.allOf.flatMap(e=>m(e,t,n));if(r.type===`array`||r.items){let e=n?`${n}[]`:`[]`;return m(r.items,t,e)}let i=r.properties||{};return Object.keys(i).length?Object.entries(i).flatMap(([e,r])=>m(r,t,n?`${n}.${e}`:e)):n?[{path:n,description:r.description,deprecated:r.deprecated}]:[]}function h(e){return e===`query`?`QUERY`:e===`path`?`PATH`:e===`header`?`HEADER`:null}function g(e,t){let n=new Map,r=Array.isArray(e.parameters)?e.parameters:[];for(let e of r){let r=d(e,t),i=h(r?.in);if(!r||!i||!r.name)continue;let a=m(r.schema,t,r.name),o=a.length?a:[{path:r.name,description:r.description,deprecated:r.deprecated}];for(let e of o)n.set(`${i}:${e.path}`,{path:e.path,description:e.description||r.description,deprecated:e.deprecated??r.deprecated??!1,type:i})}let i=d(e.requestBody,t);if(i?.content){let e=p(i.content,t);for(let r of m(e,t))n.set(`BODY:${r.path}`,{path:r.path,description:r.description||i.description,deprecated:r.deprecated??!1,type:`BODY`})}return[...n.values()]}function _(e,t){let n=new Map,r=e?.responses||{};for(let[e,i]of Object.entries(r)){if(!f(e))continue;let r=d(i,t),a=p(r?.content,t);for(let e of m(a,t))n.set(e.path,{path:e.path,description:e.description||r?.description,deprecated:e.deprecated??!1,type:`RESPONSE_BODY`})}return[...n.values()]}function v(e){let t=new Map;for(let[n,r]of Object.entries(e.paths||{}))for(let[i,a]of Object.entries(r||{})){if(!/^(get|post|put|patch|delete|head|options)$/i.test(i))continue;let r=i.toUpperCase();for(let i of[...g(a,e),..._(a,e)]){let e=`${r}:${n}:${i.path}`;t.has(e)||t.set(e,{route:n,method:r,field:i.path,description:i.description})}}return[...t.values()]}async function y(e){return v(await u(e.url,e.timeoutMs))}export{t as cmsI18nFieldRule,o as defineBackendSource,a as defineConfig,e as defineExpressionRule,s as httpClient,y as openapiSource,n as quableI18nFieldRule};
1
+ import{t as e}from"./defineExpressionRule-Dfvzj6n2.mjs";import{cmsI18nFieldRule as t}from"./rules/cmsI18nFieldRule.mjs";import{quableI18nFieldRule as n}from"./rules/quableI18nFieldRule.mjs";import{AsyncLocalStorage as r}from"node:async_hooks";import{spinner as i}from"@clack/prompts";function a(e){return e}function o(e){return e}function s(e,t){return process.env[e]??t}const c=new class{async fetch(e,t){let n={accept:`application/json`,...t?.headers},r=await fetch(e,{...t,headers:n});if(!r.ok)throw Error(`Unable to fetch ${e}: ${r.status} ${r.statusText}`);if(t?.onProgress){let e=r.body?.getReader();if(!e)return r.json();let n=new TextDecoder,i=``;for(;;){let{done:r,value:a}=await e.read();if(r)break;i+=n.decode(a,{stream:!0}),t.onProgress(i)}return i+n.decode()}return r}async get(e,t){return(await this.fetch(e,t)).json()}};function l(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(1)}s`}const u=new class{#e=new r;attach(e,t){return this.#e.run(e,t)}log(e){this.#e.getStore()?.log(e)}report(e){let t=this.#e.getStore();(t?.report||t?.log)?.(e)}createStepProgress(e=e=>({id:e,title:e})){let t=i(),n,r=e=>{if(!n)return;let r=l(Date.now()-n.startedAt);t.stop(`${e||n.completedTitle||`${n.title} completed`} (${r})`),n=void 0},a=e=>{if(n?.id===e.id){t.message(e.detail?`${e.title}: ${e.detail}`:e.title);return}r(),n={id:e.id,title:e.title,completedTitle:e.completedTitle,startedAt:Date.now()},t.start(e.detail?`${e.title}: ${e.detail}`:e.title)},o=r=>this.attach({log:e=>t.message(n?`${n.title}: ${e}`:e),report:t=>a(e(t))},r);return{report(t){a(e(t))},start:a,execute:o,run:async(e,t)=>{a(e),await new Promise(e=>setImmediate(e));let n=await o(t);return r(),n},finish(e){r(e)},fail(e){if(!n)return;let r=l(Date.now()-n.startedAt);t.stop(`${e} (${r})`),n=void 0}}}};async function d(e,t){try{let n=await c.fetch(e,{signal:t?AbortSignal.timeout(t):void 0,onProgress(e){u.log(`Downloading (${(e.length/1048576).toFixed(1)} MB)`)}});return!n||typeof n==`object`?n:(u.log(`Parsing document`),JSON.parse(n))}catch(n){throw n instanceof Error&&(n.name===`AbortError`||n.name===`TimeoutError`)?Error(`OpenAPI download timed out after ${t}ms: ${e}`):Error(`Unable to fetch OpenAPI document from ${e}: ${n instanceof Error?n.message:String(n)}`)}}function f(e,t){if(!e||typeof e!=`object`||typeof e.$ref!=`string`||!e.$ref.startsWith(`#/`))return e;let n=t;for(let t of e.$ref.replace(`#/`,``).split(`/`).map(e=>e.replace(/~1/g,`/`).replace(/~0/g,`~`)))if(n=n?.[t],n===void 0)return e;return n}function p(e){return/^\d+$/.test(e)&&Number(e)>=200&&Number(e)<=299}function m(e,t){if(!e||typeof e!=`object`)return;if(e[`application/json`]?.schema)return f(e[`application/json`].schema,t);let n=Object.values(e).find(e=>e?.schema);return n?f(n.schema,t):void 0}function h(e,t,n=``){let r=f(e,t);if(!r||typeof r!=`object`)return[];if(Array.isArray(r.allOf))return r.allOf.flatMap(e=>h(e,t,n));if(r.type===`array`||r.items){let e=n?`${n}[]`:`[]`;return h(r.items,t,e)}let i=r.properties||{};return Object.keys(i).length?Object.entries(i).flatMap(([e,r])=>h(r,t,n?`${n}.${e}`:e)):n?[{path:n,description:r.description,deprecated:r.deprecated}]:[]}function g(e){return e===`query`?`QUERY`:e===`path`?`PATH`:e===`header`?`HEADER`:null}function _(e,t){let n=new Map,r=Array.isArray(e.parameters)?e.parameters:[];for(let e of r){let r=f(e,t),i=g(r?.in);if(!r||!i||!r.name)continue;let a=h(r.schema,t,r.name),o=a.length?a:[{path:r.name,description:r.description,deprecated:r.deprecated}];for(let e of o)n.set(`${i}:${e.path}`,{path:e.path,description:e.description||r.description,deprecated:e.deprecated??r.deprecated??!1,type:i})}let i=f(e.requestBody,t);if(i?.content){let e=m(i.content,t);for(let r of h(e,t))n.set(`BODY:${r.path}`,{path:r.path,description:r.description||i.description,deprecated:r.deprecated??!1,type:`BODY`})}return[...n.values()]}function v(e,t){let n=new Map,r=e?.responses||{};for(let[e,i]of Object.entries(r)){if(!p(e))continue;let r=f(i,t),a=m(r?.content,t);for(let e of h(a,t))n.set(e.path,{path:e.path,description:e.description||r?.description,deprecated:e.deprecated??!1,type:`RESPONSE_BODY`})}return[...n.values()]}function y(e){let t=new Map;for(let[n,r]of Object.entries(e.paths||{}))for(let[i,a]of Object.entries(r||{})){if(!/^(get|post|put|patch|delete|head|options)$/i.test(i))continue;let r=i.toUpperCase();for(let i of[..._(a,e),...v(a,e)]){let e=`${r}:${n}:${i.path}`;t.has(e)||t.set(e,{route:n,method:r,field:i.path,description:i.description})}}return[...t.values()]}async function b(e){return y(await d(e.url,e.timeoutMs))}export{t as cmsI18nFieldRule,s as constant,o as defineBackendSource,a as defineConfig,e as defineExpressionRule,c as httpClient,b as openapiSource,n as quableI18nFieldRule};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#storage"],"sources":["../src/utils/defineConfig.ts","../src/utils/defineBackendSource.ts","../src/services/http/httpClient.ts","../src/services/tasks/taskProgressService.ts","../src/services/openapi/loadOpenApiDocument.ts","../src/services/openapi/schemaService.ts","../src/services/openapi/propertyExtractionService.ts","../src/services/openapi/openapiSource.ts"],"sourcesContent":["import type { UserConfig } from \"../models/types\";\n\nexport function defineConfig(config: UserConfig): UserConfig {\n return config;\n}\n","import type { BackendSource } from \"../models/types\";\n\nexport function defineBackendSource(source: BackendSource): BackendSource {\n return source;\n}\n","class HttpClient {\n async fetch(url: string, init: RequestInit & { onProgress: (content: string) => void }): Promise<string | unknown>;\n async fetch(url: string, init?: RequestInit): Promise<Response>;\n async fetch(url: string, init?: RequestInit & { onProgress?: (content: string) => void }): Promise<Response | string | unknown> {\n const headers = { accept: \"application/json\", ...init?.headers };\n\n const response = await fetch(url, {\n ...init,\n headers\n });\n\n if (!response.ok) {\n throw new Error(`Unable to fetch ${url}: ${response.status} ${response.statusText}`);\n }\n\n if (init?.onProgress) {\n const reader = response.body?.getReader();\n\n if (!reader) {\n return response.json() as unknown;\n }\n\n const decoder = new TextDecoder();\n let content = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n content += decoder.decode(value, { stream: true });\n\n init.onProgress(content);\n }\n\n return content + decoder.decode();\n }\n\n return response;\n }\n\n async get<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetch(url, init);\n return response.json() as Promise<T>;\n }\n}\n\nexport const httpClient = new HttpClient();\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { spinner } from \"@clack/prompts\";\n\nexport type StepDescriptor = {\n id: string;\n title: string;\n completedTitle?: string;\n detail?: string;\n};\n\ntype TaskProgressSink = {\n log: (message: string) => void;\n report?: (message: string) => void;\n};\n\nfunction formatDuration(durationMs: number): string {\n if (durationMs < 1_000) {\n return `${durationMs}ms`;\n }\n return `${(durationMs / 1_000).toFixed(1)}s`;\n}\n\nclass TaskProgressService {\n readonly #storage = new AsyncLocalStorage<TaskProgressSink>();\n\n attach<T>(sink: TaskProgressSink, task: () => Promise<T>): Promise<T> {\n return this.#storage.run(sink, task);\n }\n\n log(message: string): void {\n this.#storage.getStore()?.log(message);\n }\n\n report(message: string): void {\n const sink = this.#storage.getStore();\n const reporter = sink?.report || sink?.log;\n reporter?.(message);\n }\n\n createStepProgress(classify: (message: string) => StepDescriptor = (message) => ({ id: message, title: message })) {\n const progress = spinner();\n let active: { id: string; title: string; completedTitle?: string; startedAt: number } | undefined;\n\n const finishActive = (label?: string) => {\n if (!active) {\n return;\n }\n const duration = formatDuration(Date.now() - active.startedAt);\n progress.stop(`${label || active.completedTitle || `${active.title} completed`} (${duration})`);\n active = undefined;\n };\n\n const start = (step: StepDescriptor) => {\n if (active?.id === step.id) {\n progress.message(step.detail ? `${step.title}: ${step.detail}` : step.title);\n return;\n }\n\n finishActive();\n active = {\n id: step.id,\n title: step.title,\n completedTitle: step.completedTitle,\n startedAt: Date.now()\n };\n progress.start(step.detail ? `${step.title}: ${step.detail}` : step.title);\n };\n\n const execute = <T>(task: () => Promise<T>): Promise<T> =>\n this.attach(\n {\n log: (message) => progress.message(active ? `${active.title}: ${message}` : message),\n report: (message) => start(classify(message))\n },\n task\n );\n\n return {\n report(message: string) {\n start(classify(message));\n },\n start,\n execute,\n run: async <T>(step: StepDescriptor, task: () => Promise<T>): Promise<T> => {\n start(step);\n await new Promise<void>((resolve) => setImmediate(resolve));\n const result = await execute(task);\n finishActive();\n return result;\n },\n finish(label?: string) {\n finishActive(label);\n },\n fail(label: string) {\n if (!active) {\n return;\n }\n const duration = formatDuration(Date.now() - active.startedAt);\n progress.stop(`${label} (${duration})`);\n active = undefined;\n }\n };\n }\n}\n\nexport const taskProgressService = new TaskProgressService();\n","import { httpClient } from \"../http/httpClient\";\nimport { taskProgressService } from \"../tasks/taskProgressService\";\n\nexport type OpenApiDocument = {\n info?: { version?: string };\n paths?: Record<string, Record<string, any>>;\n components?: Record<string, any>;\n};\n\nexport async function loadOpenApiDocument(url: string, timeoutMs?: number): Promise<OpenApiDocument> {\n try {\n const content = await httpClient.fetch(url, {\n signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined,\n onProgress(content) {\n taskProgressService.log(`Downloading (${(content.length / 1_048_576).toFixed(1)} MB)`);\n }\n });\n\n if (!content || typeof content === \"object\") {\n return content as OpenApiDocument;\n }\n\n taskProgressService.log(\"Parsing document\");\n\n return JSON.parse(content as string) as OpenApiDocument;\n } catch (error) {\n if (error instanceof Error && (error.name === \"AbortError\" || error.name === \"TimeoutError\")) {\n throw new Error(`OpenAPI download timed out after ${timeoutMs}ms: ${url}`);\n }\n\n throw new Error(`Unable to fetch OpenAPI document from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n","import type { OpenApiDocument } from \"./loadOpenApiDocument\";\n\nexport function resolveOpenApiReference(value: any, swagger: OpenApiDocument): any {\n if (!value || typeof value !== \"object\" || typeof value.$ref !== \"string\" || !value.$ref.startsWith(\"#/\")) {\n return value;\n }\n let current: any = swagger;\n for (const segment of value.$ref\n .replace(\"#/\", \"\")\n .split(\"/\")\n .map((part: string) => part.replace(/~1/g, \"/\").replace(/~0/g, \"~\"))) {\n current = current?.[segment];\n if (current === undefined) {\n return value;\n }\n }\n return current;\n}\n","import type { BackendProperty, ExtractedApiProperty } from \"../../models/types\";\nimport type { OpenApiDocument } from \"./loadOpenApiDocument\";\nimport { resolveOpenApiReference } from \"./schemaService\";\n\ntype OpenApiLeafProperty = {\n path: string;\n description?: string;\n deprecated?: boolean;\n};\n\nfunction isSuccessStatusCode(statusCode: string): boolean {\n return /^\\d+$/.test(statusCode) && Number(statusCode) >= 200 && Number(statusCode) <= 299;\n}\n\nfunction selectPreferredSchema(content: any, swagger: OpenApiDocument): any {\n if (!content || typeof content !== \"object\") {\n return undefined;\n }\n\n if (content[\"application/json\"]?.schema) {\n return resolveOpenApiReference(content[\"application/json\"].schema, swagger);\n }\n\n const firstSchema = Object.values(content).find((entry: any) => entry?.schema) as any;\n return firstSchema ? resolveOpenApiReference(firstSchema.schema, swagger) : undefined;\n}\n\nfunction extractLeafProperties(schema: any, swagger: OpenApiDocument, currentPath = \"\"): OpenApiLeafProperty[] {\n const resolvedSchema = resolveOpenApiReference(schema, swagger);\n\n if (!resolvedSchema || typeof resolvedSchema !== \"object\") {\n return [];\n }\n\n if (Array.isArray(resolvedSchema.allOf)) {\n return resolvedSchema.allOf.flatMap((item: any) => extractLeafProperties(item, swagger, currentPath));\n }\n\n if (resolvedSchema.type === \"array\" || resolvedSchema.items) {\n const arrayPath = currentPath ? `${currentPath}[]` : \"[]\";\n return extractLeafProperties(resolvedSchema.items, swagger, arrayPath);\n }\n\n const properties = resolvedSchema.properties || {};\n if (!Object.keys(properties).length) {\n return currentPath\n ? [\n {\n path: currentPath,\n description: resolvedSchema.description,\n deprecated: resolvedSchema.deprecated\n }\n ]\n : [];\n }\n\n return Object.entries(properties).flatMap(([propertyName, propertySchema]: [string, any]) => {\n const nextPath = currentPath ? `${currentPath}.${propertyName}` : propertyName;\n return extractLeafProperties(propertySchema, swagger, nextPath);\n });\n}\n\nfunction mapInputType(inType?: string): ExtractedApiProperty[\"type\"] | null {\n if (inType === \"query\") {\n return \"QUERY\";\n }\n if (inType === \"path\") {\n return \"PATH\";\n }\n if (inType === \"header\") {\n return \"HEADER\";\n }\n return null;\n}\n\nexport function extractOpenApiInputProperties(operation: any, swagger: OpenApiDocument): ExtractedApiProperty[] {\n const map = new Map<string, ExtractedApiProperty>();\n const parameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n\n for (const rawParameter of parameters) {\n const parameter = resolveOpenApiReference(rawParameter, swagger);\n const inputType = mapInputType(parameter?.in);\n if (!parameter || !inputType || !parameter.name) {\n continue;\n }\n\n const properties = extractLeafProperties(parameter.schema, swagger, parameter.name);\n const resolvedProperties = properties.length\n ? properties\n : [\n {\n path: parameter.name,\n description: parameter.description,\n deprecated: parameter.deprecated\n }\n ];\n\n for (const property of resolvedProperties) {\n map.set(`${inputType}:${property.path}`, {\n path: property.path,\n description: property.description || parameter.description,\n deprecated: property.deprecated ?? parameter.deprecated ?? false,\n type: inputType\n });\n }\n }\n\n const requestBody = resolveOpenApiReference(operation.requestBody, swagger);\n if (requestBody?.content) {\n const schema = selectPreferredSchema(requestBody.content, swagger);\n for (const property of extractLeafProperties(schema, swagger)) {\n map.set(`BODY:${property.path}`, {\n path: property.path,\n description: property.description || requestBody.description,\n deprecated: property.deprecated ?? false,\n type: \"BODY\"\n });\n }\n }\n\n return [...map.values()];\n}\n\nexport function extractOpenApiOutputProperties(operation: any, swagger: OpenApiDocument): ExtractedApiProperty[] {\n const map = new Map<string, ExtractedApiProperty>();\n const responses = operation?.responses || {};\n\n for (const [statusCode, rawResponse] of Object.entries(responses)) {\n if (!isSuccessStatusCode(statusCode)) {\n continue;\n }\n\n const response = resolveOpenApiReference(rawResponse, swagger);\n const schema = selectPreferredSchema(response?.content, swagger);\n for (const property of extractLeafProperties(schema, swagger)) {\n map.set(property.path, {\n path: property.path,\n description: property.description || response?.description,\n deprecated: property.deprecated ?? false,\n type: \"RESPONSE_BODY\"\n });\n }\n }\n\n return [...map.values()];\n}\n\nexport function extractOpenApiBackendProperties(swagger: OpenApiDocument): BackendProperty[] {\n const properties = new Map<string, BackendProperty>();\n for (const [route, pathItem] of Object.entries(swagger.paths || {})) {\n for (const [rawMethod, operation] of Object.entries(pathItem || {})) {\n if (!/^(get|post|put|patch|delete|head|options)$/i.test(rawMethod)) {\n continue;\n }\n const method = rawMethod.toUpperCase();\n for (const property of [\n ...extractOpenApiInputProperties(operation, swagger),\n ...extractOpenApiOutputProperties(operation, swagger)\n ]) {\n const key = `${method}:${route}:${property.path}`;\n if (!properties.has(key)) {\n properties.set(key, {\n route,\n method,\n field: property.path,\n description: property.description\n });\n }\n }\n }\n }\n return [...properties.values()];\n}\n","import type { BackendProperty } from \"../../models/types\";\nimport { loadOpenApiDocument } from \"./loadOpenApiDocument\";\nimport { extractOpenApiBackendProperties } from \"./propertyExtractionService\";\n\nexport async function openapiSource(params: { url: string; timeoutMs?: number }): Promise<BackendProperty[]> {\n const document = await loadOpenApiDocument(params.url, params.timeoutMs);\n return extractOpenApiBackendProperties(document);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["#storage"],"sources":["../src/utils/defineConfig.ts","../src/utils/defineBackendSource.ts","../src/utils/constant.ts","../src/services/http/httpClient.ts","../src/services/tasks/taskProgressService.ts","../src/services/openapi/loadOpenApiDocument.ts","../src/services/openapi/schemaService.ts","../src/services/openapi/propertyExtractionService.ts","../src/services/openapi/openapiSource.ts"],"sourcesContent":["import type { UserConfig } from \"../models/types\";\n\nexport function defineConfig(config: UserConfig): UserConfig {\n return config;\n}\n","import type { BackendSource } from \"../models/types\";\n\nexport function defineBackendSource(source: BackendSource): BackendSource {\n return source;\n}\n","/**\n * Resolves an environment variable, falling back to the supplied default value.\n *\n * This is intended for values declared directly in an Atlas configuration, such\n * as URLs or tokens used by a backend source.\n */\nexport function constant<T extends string = string>(name: string, defaultValue: T): string {\n return process.env[name] ?? defaultValue;\n}\n","class HttpClient {\n async fetch(url: string, init: RequestInit & { onProgress: (content: string) => void }): Promise<string | unknown>;\n async fetch(url: string, init?: RequestInit): Promise<Response>;\n async fetch(url: string, init?: RequestInit & { onProgress?: (content: string) => void }): Promise<Response | string | unknown> {\n const headers = { accept: \"application/json\", ...init?.headers };\n\n const response = await fetch(url, {\n ...init,\n headers\n });\n\n if (!response.ok) {\n throw new Error(`Unable to fetch ${url}: ${response.status} ${response.statusText}`);\n }\n\n if (init?.onProgress) {\n const reader = response.body?.getReader();\n\n if (!reader) {\n return response.json() as unknown;\n }\n\n const decoder = new TextDecoder();\n let content = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n content += decoder.decode(value, { stream: true });\n\n init.onProgress(content);\n }\n\n return content + decoder.decode();\n }\n\n return response;\n }\n\n async get<T>(url: string, init?: RequestInit): Promise<T> {\n const response = await this.fetch(url, init);\n return response.json() as Promise<T>;\n }\n}\n\nexport const httpClient = new HttpClient();\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { spinner } from \"@clack/prompts\";\n\nexport type StepDescriptor = {\n id: string;\n title: string;\n completedTitle?: string;\n detail?: string;\n};\n\ntype TaskProgressSink = {\n log: (message: string) => void;\n report?: (message: string) => void;\n};\n\nfunction formatDuration(durationMs: number): string {\n if (durationMs < 1_000) {\n return `${durationMs}ms`;\n }\n return `${(durationMs / 1_000).toFixed(1)}s`;\n}\n\nclass TaskProgressService {\n readonly #storage = new AsyncLocalStorage<TaskProgressSink>();\n\n attach<T>(sink: TaskProgressSink, task: () => Promise<T>): Promise<T> {\n return this.#storage.run(sink, task);\n }\n\n log(message: string): void {\n this.#storage.getStore()?.log(message);\n }\n\n report(message: string): void {\n const sink = this.#storage.getStore();\n const reporter = sink?.report || sink?.log;\n reporter?.(message);\n }\n\n createStepProgress(classify: (message: string) => StepDescriptor = (message) => ({ id: message, title: message })) {\n const progress = spinner();\n let active: { id: string; title: string; completedTitle?: string; startedAt: number } | undefined;\n\n const finishActive = (label?: string) => {\n if (!active) {\n return;\n }\n const duration = formatDuration(Date.now() - active.startedAt);\n progress.stop(`${label || active.completedTitle || `${active.title} completed`} (${duration})`);\n active = undefined;\n };\n\n const start = (step: StepDescriptor) => {\n if (active?.id === step.id) {\n progress.message(step.detail ? `${step.title}: ${step.detail}` : step.title);\n return;\n }\n\n finishActive();\n active = {\n id: step.id,\n title: step.title,\n completedTitle: step.completedTitle,\n startedAt: Date.now()\n };\n progress.start(step.detail ? `${step.title}: ${step.detail}` : step.title);\n };\n\n const execute = <T>(task: () => Promise<T>): Promise<T> =>\n this.attach(\n {\n log: (message) => progress.message(active ? `${active.title}: ${message}` : message),\n report: (message) => start(classify(message))\n },\n task\n );\n\n return {\n report(message: string) {\n start(classify(message));\n },\n start,\n execute,\n run: async <T>(step: StepDescriptor, task: () => Promise<T>): Promise<T> => {\n start(step);\n await new Promise<void>((resolve) => setImmediate(resolve));\n const result = await execute(task);\n finishActive();\n return result;\n },\n finish(label?: string) {\n finishActive(label);\n },\n fail(label: string) {\n if (!active) {\n return;\n }\n const duration = formatDuration(Date.now() - active.startedAt);\n progress.stop(`${label} (${duration})`);\n active = undefined;\n }\n };\n }\n}\n\nexport const taskProgressService = new TaskProgressService();\n","import { httpClient } from \"../http/httpClient\";\nimport { taskProgressService } from \"../tasks/taskProgressService\";\n\nexport type OpenApiDocument = {\n info?: { version?: string };\n paths?: Record<string, Record<string, any>>;\n components?: Record<string, any>;\n};\n\nexport async function loadOpenApiDocument(url: string, timeoutMs?: number): Promise<OpenApiDocument> {\n try {\n const content = await httpClient.fetch(url, {\n signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined,\n onProgress(content) {\n taskProgressService.log(`Downloading (${(content.length / 1_048_576).toFixed(1)} MB)`);\n }\n });\n\n if (!content || typeof content === \"object\") {\n return content as OpenApiDocument;\n }\n\n taskProgressService.log(\"Parsing document\");\n\n return JSON.parse(content as string) as OpenApiDocument;\n } catch (error) {\n if (error instanceof Error && (error.name === \"AbortError\" || error.name === \"TimeoutError\")) {\n throw new Error(`OpenAPI download timed out after ${timeoutMs}ms: ${url}`);\n }\n\n throw new Error(`Unable to fetch OpenAPI document from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n","import type { OpenApiDocument } from \"./loadOpenApiDocument\";\n\nexport function resolveOpenApiReference(value: any, swagger: OpenApiDocument): any {\n if (!value || typeof value !== \"object\" || typeof value.$ref !== \"string\" || !value.$ref.startsWith(\"#/\")) {\n return value;\n }\n let current: any = swagger;\n for (const segment of value.$ref\n .replace(\"#/\", \"\")\n .split(\"/\")\n .map((part: string) => part.replace(/~1/g, \"/\").replace(/~0/g, \"~\"))) {\n current = current?.[segment];\n if (current === undefined) {\n return value;\n }\n }\n return current;\n}\n","import type { BackendProperty, ExtractedApiProperty } from \"../../models/types\";\nimport type { OpenApiDocument } from \"./loadOpenApiDocument\";\nimport { resolveOpenApiReference } from \"./schemaService\";\n\ntype OpenApiLeafProperty = {\n path: string;\n description?: string;\n deprecated?: boolean;\n};\n\nfunction isSuccessStatusCode(statusCode: string): boolean {\n return /^\\d+$/.test(statusCode) && Number(statusCode) >= 200 && Number(statusCode) <= 299;\n}\n\nfunction selectPreferredSchema(content: any, swagger: OpenApiDocument): any {\n if (!content || typeof content !== \"object\") {\n return undefined;\n }\n\n if (content[\"application/json\"]?.schema) {\n return resolveOpenApiReference(content[\"application/json\"].schema, swagger);\n }\n\n const firstSchema = Object.values(content).find((entry: any) => entry?.schema) as any;\n return firstSchema ? resolveOpenApiReference(firstSchema.schema, swagger) : undefined;\n}\n\nfunction extractLeafProperties(schema: any, swagger: OpenApiDocument, currentPath = \"\"): OpenApiLeafProperty[] {\n const resolvedSchema = resolveOpenApiReference(schema, swagger);\n\n if (!resolvedSchema || typeof resolvedSchema !== \"object\") {\n return [];\n }\n\n if (Array.isArray(resolvedSchema.allOf)) {\n return resolvedSchema.allOf.flatMap((item: any) => extractLeafProperties(item, swagger, currentPath));\n }\n\n if (resolvedSchema.type === \"array\" || resolvedSchema.items) {\n const arrayPath = currentPath ? `${currentPath}[]` : \"[]\";\n return extractLeafProperties(resolvedSchema.items, swagger, arrayPath);\n }\n\n const properties = resolvedSchema.properties || {};\n if (!Object.keys(properties).length) {\n return currentPath\n ? [\n {\n path: currentPath,\n description: resolvedSchema.description,\n deprecated: resolvedSchema.deprecated\n }\n ]\n : [];\n }\n\n return Object.entries(properties).flatMap(([propertyName, propertySchema]: [string, any]) => {\n const nextPath = currentPath ? `${currentPath}.${propertyName}` : propertyName;\n return extractLeafProperties(propertySchema, swagger, nextPath);\n });\n}\n\nfunction mapInputType(inType?: string): ExtractedApiProperty[\"type\"] | null {\n if (inType === \"query\") {\n return \"QUERY\";\n }\n if (inType === \"path\") {\n return \"PATH\";\n }\n if (inType === \"header\") {\n return \"HEADER\";\n }\n return null;\n}\n\nexport function extractOpenApiInputProperties(operation: any, swagger: OpenApiDocument): ExtractedApiProperty[] {\n const map = new Map<string, ExtractedApiProperty>();\n const parameters = Array.isArray(operation.parameters) ? operation.parameters : [];\n\n for (const rawParameter of parameters) {\n const parameter = resolveOpenApiReference(rawParameter, swagger);\n const inputType = mapInputType(parameter?.in);\n if (!parameter || !inputType || !parameter.name) {\n continue;\n }\n\n const properties = extractLeafProperties(parameter.schema, swagger, parameter.name);\n const resolvedProperties = properties.length\n ? properties\n : [\n {\n path: parameter.name,\n description: parameter.description,\n deprecated: parameter.deprecated\n }\n ];\n\n for (const property of resolvedProperties) {\n map.set(`${inputType}:${property.path}`, {\n path: property.path,\n description: property.description || parameter.description,\n deprecated: property.deprecated ?? parameter.deprecated ?? false,\n type: inputType\n });\n }\n }\n\n const requestBody = resolveOpenApiReference(operation.requestBody, swagger);\n if (requestBody?.content) {\n const schema = selectPreferredSchema(requestBody.content, swagger);\n for (const property of extractLeafProperties(schema, swagger)) {\n map.set(`BODY:${property.path}`, {\n path: property.path,\n description: property.description || requestBody.description,\n deprecated: property.deprecated ?? false,\n type: \"BODY\"\n });\n }\n }\n\n return [...map.values()];\n}\n\nexport function extractOpenApiOutputProperties(operation: any, swagger: OpenApiDocument): ExtractedApiProperty[] {\n const map = new Map<string, ExtractedApiProperty>();\n const responses = operation?.responses || {};\n\n for (const [statusCode, rawResponse] of Object.entries(responses)) {\n if (!isSuccessStatusCode(statusCode)) {\n continue;\n }\n\n const response = resolveOpenApiReference(rawResponse, swagger);\n const schema = selectPreferredSchema(response?.content, swagger);\n for (const property of extractLeafProperties(schema, swagger)) {\n map.set(property.path, {\n path: property.path,\n description: property.description || response?.description,\n deprecated: property.deprecated ?? false,\n type: \"RESPONSE_BODY\"\n });\n }\n }\n\n return [...map.values()];\n}\n\nexport function extractOpenApiBackendProperties(swagger: OpenApiDocument): BackendProperty[] {\n const properties = new Map<string, BackendProperty>();\n for (const [route, pathItem] of Object.entries(swagger.paths || {})) {\n for (const [rawMethod, operation] of Object.entries(pathItem || {})) {\n if (!/^(get|post|put|patch|delete|head|options)$/i.test(rawMethod)) {\n continue;\n }\n const method = rawMethod.toUpperCase();\n for (const property of [\n ...extractOpenApiInputProperties(operation, swagger),\n ...extractOpenApiOutputProperties(operation, swagger)\n ]) {\n const key = `${method}:${route}:${property.path}`;\n if (!properties.has(key)) {\n properties.set(key, {\n route,\n method,\n field: property.path,\n description: property.description\n });\n }\n }\n }\n }\n return [...properties.values()];\n}\n","import type { BackendProperty } from \"../../models/types\";\nimport { loadOpenApiDocument } from \"./loadOpenApiDocument\";\nimport { extractOpenApiBackendProperties } from \"./propertyExtractionService\";\n\nexport async function openapiSource(params: { url: string; timeoutMs?: number }): Promise<BackendProperty[]> {\n const document = await loadOpenApiDocument(params.url, params.timeoutMs);\n return extractOpenApiBackendProperties(document);\n}\n"],"mappings":"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"}
@@ -16,19 +16,24 @@ function setUserConfig(config) {
16
16
  analysis: {
17
17
  ...config.analysis,
18
18
  backends,
19
- rules: [.../* @__PURE__ */ new Set([...config.analysis.rules, ...backends.flatMap((backend) => backend.rules || [])])]
19
+ rules: normalizeExpressionRules([...config.analysis.rules, ...backends.flatMap((backend) => backend.rules || [])])
20
20
  }
21
21
  };
22
22
  }
23
23
  function normalizeBackendSources(backends) {
24
24
  const sources = backends.map((backend) => typeof backend === "string" ? { name: backend } : backend);
25
- const names = /* @__PURE__ */ new Set();
25
+ const sourcesByName = /* @__PURE__ */ new Map();
26
26
  for (const source of sources) {
27
27
  if (!source.name.trim()) throw new Error("Backend source names must not be empty");
28
- if (names.has(source.name)) throw new Error(`Backend source names must be unique: ${source.name}`);
29
- names.add(source.name);
28
+ sourcesByName.set(source.name, source);
30
29
  }
31
- return sources;
30
+ return [...sourcesByName.values()];
31
+ }
32
+ function normalizeExpressionRules(rules) {
33
+ return [...new Set(rules)].map((rule, index) => ({
34
+ rule,
35
+ index
36
+ })).sort((left, right) => (right.rule.priority || 0) - (left.rule.priority || 0) || left.index - right.index).map(({ rule }) => rule);
32
37
  }
33
38
  function getUserConfig() {
34
39
  if (!_config) throw new Error("Atlas config not loaded. Run Atlas from a directory containing atlas.config.ts or pass --config.");
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/cleanObjectRule.d.ts
3
3
  declare const cleanObjectRule: FieldExtractionRule;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/cmsI18nFieldRule.d.ts
3
3
  /**
4
4
  * `CmsI18n#get`/`getAll` reads a localized field off the translations array passed to the
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/dateConversionRule.d.ts
3
3
  declare const dateConversionRule: FieldExtractionRule;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/lodashGetRule.d.ts
3
3
  declare const lodashGetRule: FieldExtractionRule;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/mappingUtilityRule.d.ts
3
3
  declare const mappingUtilityRule: FieldExtractionRule;
4
4
  //#endregion
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/memberGetFieldRule.d.ts
3
3
  declare const memberGetFieldRule: FieldExtractionRule;
4
4
  //#endregion
@@ -1,2 +1,2 @@
1
- import{t as e}from"../defineExpressionRule-Dfvzj6n2.mjs";import{Node as t}from"ts-morph";const n=e({name:`member-get-field`,match:e=>{if(!t.isCallExpression(e))return!1;let n=e.getExpression(),r=e.getArguments().find(e=>t.isStringLiteral(e)||t.isNoSubstitutionTemplateLiteral(e));return t.isPropertyAccessExpression(n)&&[`get`,`getAll`].includes(n.getName())&&!!r},parse:e=>{if(!t.isCallExpression(e))return;let n=e.getExpression(),r=e.getArguments().find(e=>t.isStringLiteral(e)||t.isNoSubstitutionTemplateLiteral(e));if(!(!t.isPropertyAccessExpression(n)||!t.isStringLiteral(r)&&!t.isNoSubstitutionTemplateLiteral(r)))return{backendField:`${n.getExpression().getText()}.${r.getLiteralValue()}`}}});export{n as memberGetFieldRule};
1
+ import{t as e}from"../defineExpressionRule-Dfvzj6n2.mjs";import{Node as t}from"ts-morph";const n=e({name:`member-get-field`,priority:-100,match:e=>{if(!t.isCallExpression(e))return!1;let n=e.getExpression(),r=e.getArguments().find(e=>t.isStringLiteral(e)||t.isNoSubstitutionTemplateLiteral(e));return t.isPropertyAccessExpression(n)&&[`get`,`getAll`].includes(n.getName())&&!!r},parse:e=>{if(!t.isCallExpression(e))return;let n=e.getExpression(),r=e.getArguments().find(e=>t.isStringLiteral(e)||t.isNoSubstitutionTemplateLiteral(e));if(!(!t.isPropertyAccessExpression(n)||!t.isStringLiteral(r)&&!t.isNoSubstitutionTemplateLiteral(r)))return{backendField:`${n.getExpression().getText()}.${r.getLiteralValue()}`}}});export{n as memberGetFieldRule};
2
2
  //# sourceMappingURL=memberGetFieldRule.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"memberGetFieldRule.mjs","names":[],"sources":["../../src/rules/memberGetFieldRule.ts"],"sourcesContent":["import { Node } from \"ts-morph\";\nimport { defineExpressionRule } from \"../utils/defineExpressionRule\";\n\nexport const memberGetFieldRule = defineExpressionRule({\n name: \"member-get-field\",\n match: (expression) => {\n if (!Node.isCallExpression(expression)) {\n return false;\n }\n\n const target = expression.getExpression();\n const field = expression\n .getArguments()\n .find((argument) => Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument));\n return Node.isPropertyAccessExpression(target) && [\"get\", \"getAll\"].includes(target.getName()) && Boolean(field);\n },\n parse: (expression) => {\n if (!Node.isCallExpression(expression)) {\n return undefined;\n }\n\n const target = expression.getExpression();\n const field = expression\n .getArguments()\n .find((argument) => Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument));\n if (!Node.isPropertyAccessExpression(target) || (!Node.isStringLiteral(field) && !Node.isNoSubstitutionTemplateLiteral(field))) {\n return undefined;\n }\n\n return { backendField: `${target.getExpression().getText()}.${field.getLiteralValue()}` };\n }\n});\n"],"mappings":"yFAGA,MAAa,EAAqB,EAAqB,CACrD,KAAM,mBACN,MAAQ,GAAe,CACrB,GAAI,CAAC,EAAK,iBAAiB,CAAU,EACnC,MAAO,GAGT,IAAM,EAAS,EAAW,cAAc,EAClC,EAAQ,EACX,aAAa,CAAC,CACd,KAAM,GAAa,EAAK,gBAAgB,CAAQ,GAAK,EAAK,gCAAgC,CAAQ,CAAC,EACtG,OAAO,EAAK,2BAA2B,CAAM,GAAK,CAAC,MAAO,QAAQ,CAAC,CAAC,SAAS,EAAO,QAAQ,CAAC,GAAK,EAAQ,CAC5G,EACA,MAAQ,GAAe,CACrB,GAAI,CAAC,EAAK,iBAAiB,CAAU,EACnC,OAGF,IAAM,EAAS,EAAW,cAAc,EAClC,EAAQ,EACX,aAAa,CAAC,CACd,KAAM,GAAa,EAAK,gBAAgB,CAAQ,GAAK,EAAK,gCAAgC,CAAQ,CAAC,EAClG,MAAC,EAAK,2BAA2B,CAAM,GAAM,CAAC,EAAK,gBAAgB,CAAK,GAAK,CAAC,EAAK,gCAAgC,CAAK,GAI5H,MAAO,CAAE,aAAc,GAAG,EAAO,cAAc,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAM,gBAAgB,GAAI,CAC1F,CACF,CAAC"}
1
+ {"version":3,"file":"memberGetFieldRule.mjs","names":[],"sources":["../../src/rules/memberGetFieldRule.ts"],"sourcesContent":["import { Node } from \"ts-morph\";\nimport { defineExpressionRule } from \"../utils/defineExpressionRule\";\n\nexport const memberGetFieldRule = defineExpressionRule({\n name: \"member-get-field\",\n priority: -100,\n match: (expression) => {\n if (!Node.isCallExpression(expression)) {\n return false;\n }\n\n const target = expression.getExpression();\n const field = expression\n .getArguments()\n .find((argument) => Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument));\n return Node.isPropertyAccessExpression(target) && [\"get\", \"getAll\"].includes(target.getName()) && Boolean(field);\n },\n parse: (expression) => {\n if (!Node.isCallExpression(expression)) {\n return undefined;\n }\n\n const target = expression.getExpression();\n const field = expression\n .getArguments()\n .find((argument) => Node.isStringLiteral(argument) || Node.isNoSubstitutionTemplateLiteral(argument));\n if (!Node.isPropertyAccessExpression(target) || (!Node.isStringLiteral(field) && !Node.isNoSubstitutionTemplateLiteral(field))) {\n return undefined;\n }\n\n return { backendField: `${target.getExpression().getText()}.${field.getLiteralValue()}` };\n }\n});\n"],"mappings":"yFAGA,MAAa,EAAqB,EAAqB,CACrD,KAAM,mBACN,SAAU,KACV,MAAQ,GAAe,CACrB,GAAI,CAAC,EAAK,iBAAiB,CAAU,EACnC,MAAO,GAGT,IAAM,EAAS,EAAW,cAAc,EAClC,EAAQ,EACX,aAAa,CAAC,CACd,KAAM,GAAa,EAAK,gBAAgB,CAAQ,GAAK,EAAK,gCAAgC,CAAQ,CAAC,EACtG,OAAO,EAAK,2BAA2B,CAAM,GAAK,CAAC,MAAO,QAAQ,CAAC,CAAC,SAAS,EAAO,QAAQ,CAAC,GAAK,EAAQ,CAC5G,EACA,MAAQ,GAAe,CACrB,GAAI,CAAC,EAAK,iBAAiB,CAAU,EACnC,OAGF,IAAM,EAAS,EAAW,cAAc,EAClC,EAAQ,EACX,aAAa,CAAC,CACd,KAAM,GAAa,EAAK,gBAAgB,CAAQ,GAAK,EAAK,gCAAgC,CAAQ,CAAC,EAClG,MAAC,EAAK,2BAA2B,CAAM,GAAM,CAAC,EAAK,gBAAgB,CAAK,GAAK,CAAC,EAAK,gCAAgC,CAAK,GAI5H,MAAO,CAAE,aAAc,GAAG,EAAO,cAAc,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAM,gBAAgB,GAAI,CAC1F,CACF,CAAC"}
@@ -1,4 +1,4 @@
1
- import { i as FieldExtractionRule } from "../types-smD5SZe9.mjs";
1
+ import { i as FieldExtractionRule } from "../types-cYprdLUO.mjs";
2
2
  //#region src/rules/quableI18nFieldRule.d.ts
3
3
  declare const quableI18nFieldRule: FieldExtractionRule;
4
4
  //#endregion
@@ -8,6 +8,8 @@ type FieldExtractionRuleResult = {
8
8
  };
9
9
  type FieldExtractionRule = {
10
10
  name: string;
11
+ /** Higher-priority rules are evaluated first. Defaults to 0. */
12
+ priority?: number;
11
13
  match: (expression: Expression, config: UserConfig) => boolean;
12
14
  parse: (expression: Expression, config: UserConfig) => FieldExtractionRuleResult | undefined;
13
15
  };
@@ -114,4 +116,4 @@ interface UserConfig {
114
116
  }
115
117
  //#endregion
116
118
  export { defineExpressionRule as a, FieldExtractionRule as i, BackendSource as n, UserConfig as r, BackendProperty as t };
117
- //# sourceMappingURL=types-smD5SZe9.d.mts.map
119
+ //# sourceMappingURL=types-cYprdLUO.d.mts.map
@@ -1,4 +1,4 @@
1
- import { t as generateBackendTopologyArtifacts } from "../routeBackendTopologyService-DkNyCtKt.mjs";
1
+ import { t as generateBackendTopologyArtifacts } from "../routeBackendTopologyService-DElirHSh.mjs";
2
2
  import { parentPort, workerData } from "node:worker_threads";
3
3
  //#region src/workers/routeBackendTopologyWorker.ts
4
4
  const data = workerData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmflow/atlas",
3
- "version": "3.4.0-beta.8",
3
+ "version": "3.4.0-beta.9",
4
4
  "description": "API-to-backend mapping catalogue for Club Med Flow",
5
5
  "license": "MIT",
6
6
  "author": "romakita",