@redocly/realm 0.134.0-next.2 → 0.134.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/client/app/utils/getBlocksHeight.d.ts +5 -14
  3. package/dist/client/app/utils/getBlocksHeight.js +1 -1
  4. package/dist/client/app/utils/syncScrollWithEditor.js +1 -1
  5. package/dist/server/node-bundle-entry.js +1 -1
  6. package/dist/server/persistence/cache/repositories/cache-repository.js +1 -1
  7. package/dist/server/persistence/file-hashes/repositories/file-hashes-repository.js +1 -1
  8. package/dist/server/persistence/kv/repositories/kv-repository.d.ts +1 -1
  9. package/dist/server/persistence/kv/repositories/kv-repository.js +2 -2
  10. package/dist/server/persistence/kv/services/kv-service.d.ts +3 -2
  11. package/dist/server/persistence/kv/services/kv-service.js +1 -1
  12. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.d.ts +2 -1
  13. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.js +1 -1
  14. package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.d.ts +1 -4
  15. package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.js +1 -1
  16. package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-read-repository.js +1 -1
  17. package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
  18. package/dist/server/plugins/entitlements/utils/get-billed-catalog-build-pages-count.js +1 -1
  19. package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.d.ts +4 -0
  20. package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.js +1 -1
  21. package/dist/server/plugins/scorecards/database/scorecards-config-service.d.ts +4 -4
  22. package/dist/server/plugins/scorecards/database/scorecards-config-service.js +1 -1
  23. package/dist/server/providers/database/base-service.d.ts +25 -0
  24. package/dist/server/providers/database/base-service.js +1 -0
  25. package/dist/server/providers/database/database-preconnect-service.js +1 -1
  26. package/dist/server/providers/database/instance-cache-resolver.d.ts +19 -0
  27. package/dist/server/providers/database/instance-cache-resolver.js +1 -0
  28. package/dist/server/utils/is-realm-or-reef.d.ts +2 -0
  29. package/dist/server/utils/is-realm-or-reef.js +1 -0
  30. package/dist/server/web-server/dev-server.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/server/plugins/catalog-entities/utils/has-options-changed.d.ts +0 -2
  33. package/dist/server/plugins/catalog-entities/utils/has-options-changed.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @redocly/realm
2
2
 
3
+ ## 0.134.0-next.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 98c2a8d099e: Fixed scroll synchronization between the editor and the Webview.
8
+
3
9
  ## 0.134.0-next.2
4
10
 
5
11
  ### Minor Changes
@@ -77,6 +83,15 @@
77
83
  - @redocly/graphql-docs@1.11.0-next.0
78
84
  - @redocly/openapi-docs@3.22.0-next.0
79
85
 
86
+ ## 0.133.1
87
+
88
+ ### Patch Changes
89
+
90
+ - e69c304a76: Fixed an issue where in rare cases, clicking on a link on a Markdown page, could trigger navigation twice, taking the user to the first link on the target page.
91
+ - Updated dependencies [3aa79f51c8]
92
+ - @redocly/realm-asyncapi-sdk@0.11.1
93
+ - @redocly/theme@0.65.0
94
+
80
95
  ## 0.133.0
81
96
 
82
97
  ### Minor Changes
@@ -1,24 +1,16 @@
1
- type BlockHeight = {
1
+ export type BlockHeight = {
2
2
  height: number;
3
3
  offset: number;
4
4
  };
5
5
  /**
6
- * Calculates the height of each block element within an article, accounting for margins between blocks.
7
- * A block is defined as content between two heading elements or from start/end of article.
6
+ * Calculates the height of each block element within markdown content, accounting for margins between blocks.
7
+ * A block is defined as content between two heading elements or from start/end of the content root.
8
8
  * @returns An array containing the heights and offsets of all block elements.
9
9
  */
10
10
  export declare function getBlocksHeight(): BlockHeight[];
11
11
  /**
12
- * Calculates the offset top position of a block element in an HTML document.
13
- * A block element is determined by its sequence number in relation to heading elements.
14
- * @param sequenceNumber - The sequence number of the block element (0-based index).
15
- * @returns The offset top position of the block element with the specified sequenceNumber, adjusted for nav height.
16
- * Returns 0 if the sequence number is invalid or no block is found.
17
- */
18
- export declare function getBlockOffsetTop(sequenceNumber: number): number;
19
- /**
20
- * Calculates the offset top position of an article element, adjusted for navigation height.
21
- * @returns The offset top position of the article relative to the viewport, accounting for fixed navigation.
12
+ * Calculates the offset top position of markdown content, adjusted for navigation height.
13
+ * @returns The offset top position of the content relative to the viewport, accounting for fixed navigation.
22
14
  */
23
15
  export declare const getArticleOffsetTop: () => number;
24
16
  /**
@@ -26,5 +18,4 @@ export declare const getArticleOffsetTop: () => number;
26
18
  * @returns The height of the navigation element in pixels, or 0 if no nav element exists.
27
19
  */
28
20
  export declare const getNavHeight: () => number;
29
- export {};
30
21
  //# sourceMappingURL=getBlocksHeight.d.ts.map
@@ -1 +1 @@
1
- function f(t){const o=window.getComputedStyle(t);return{marginTop:parseFloat(o.getPropertyValue("margin-top"))||0,marginBottom:parseFloat(o.getPropertyValue("margin-bottom"))||0}}const s=t=>!!t&&(t instanceof HTMLHeadingElement||/^H[1-6]$/.test(t.tagName));function p(){const t=[],o=Array.from(document.querySelectorAll("article > *")??[]);let e=0,r=l(o[0]),a=0;return o.forEach(n=>{const{marginTop:i,marginBottom:c}=f(n);s(n)&&e&&(t.push({height:e,offset:r}),r=n.offsetTop-Math.max(a,i),e=0),e+=n.offsetHeight,e+=Math.max(a,i),a=c}),e!==0&&t.push({height:e,offset:r}),t}function u(t){if(t===0)return 0;const e=Array.from(document.querySelectorAll("article > *")).filter(r=>s(r));if(t<e.length){const r=document.querySelector("nav")?.offsetHeight??0;return e[t].offsetTop-r}return 0}const m=()=>{const t=document.querySelector("article")?.offsetTop??0,o=g();return t-o},g=()=>document.querySelector("nav")?.offsetHeight??0;function l(t){if(!t)return 0;const o=window.getComputedStyle(t),e=parseFloat(o.getPropertyValue("margin-top"))||0;return t.offsetTop-e}export{m as getArticleOffsetTop,u as getBlockOffsetTop,p as getBlocksHeight,g as getNavHeight};
1
+ const g='main[data-component-name="Markdown/Markdown"]',h='[data-component-name="Markdoc/Heading/Heading"]';function a(){return document.querySelector(g)}function u(t){const e=Array.from(t.querySelectorAll(h));return e.length>0?e:Array.from(t.querySelectorAll("h1, h2, h3, h4, h5, h6"))}function l(){const t=a();if(!t)return[];const e=u(t);if(e.length===0)return[{offset:n(t),height:t.offsetHeight}];const o=[],f=n(t),s=n(e[0]);s>f&&o.push({offset:f,height:s-f});for(let r=0;r<e.length;r++){const c=n(e[r]),i=(r+1<e.length?n(e[r+1]):n(t)+t.offsetHeight)-c;i>0&&o.push({height:i,offset:c})}return o}const m=()=>{const t=a(),e=t?n(t):0,o=d();return e-o},d=()=>document.querySelector("nav")?.offsetHeight??0;function n(t){if(!t)return 0;const e=window.getComputedStyle(t),o=parseFloat(e.getPropertyValue("margin-top"))||0;return t.offsetTop-o}export{m as getArticleOffsetTop,l as getBlocksHeight,d as getNavHeight};
@@ -1 +1 @@
1
- import{getBlocksHeight as s,getArticleOffsetTop as f,getNavHeight as h}from"./getBlocksHeight.js";function g(t,e=0){if(typeof t>"u"){window.scrollTo({top:0,behavior:"smooth"});return}const o=s(),n=h();if(!o[e])return;const{height:r,offset:c}=o[e],i=Math.round(c-n+r*(Math.round(t)/100));window.scrollTo({top:i,behavior:"smooth"})}const p=t=>{const e=f(),o=h();if(t<e)return{percentScrolled:0,sequenceNumber:-1};const n=s(),r=l(t+o,n),{height:c,offset:i}=n[r];return{percentScrolled:Math.round((t+o-i)/c*100),sequenceNumber:r}};function l(t,e){return e.findIndex(({offset:o,height:n})=>t>=o&&t<o+n)}export{g as syncScrollWithEditor,p as syncScrollWithProject};
1
+ import{getBlocksHeight as s,getArticleOffsetTop as f,getNavHeight as u}from"./getBlocksHeight.js";function m(t,n=0){if(typeof t>"u"){window.scrollTo({top:0,behavior:"smooth"});return}const o=s(),e=u();if(!o[n])return;const{height:r,offset:c}=o[n],i=Math.round(c-e+r*(Math.round(t)/100));window.scrollTo({top:i,behavior:"instant"})}const p=t=>{const n=f(),o=u();if(t<n)return{percentScrolled:0,sequenceNumber:-1};const e=s(),r=h(t+o,e);if(r===-1||!e[r])return{percentScrolled:0,sequenceNumber:-1};const{height:c,offset:i}=e[r];return c<=0?{percentScrolled:0,sequenceNumber:-1}:{percentScrolled:l(Math.round((t+o-i)/c*100)),sequenceNumber:r}};function l(t){return Math.max(0,Math.min(100,t))}function h(t,n){const o=n.findIndex(({offset:e,height:r})=>t>=e&&t<e+r);if(o!==-1)return o;for(let e=n.length-1;e>=0;e--)if(t>=n[e].offset)return e;return-1}export{m as syncScrollWithEditor,p as syncScrollWithProject};
@@ -1 +1 @@
1
- import{resolve as n,dirname as f}from"node:path";import{fileURLToPath as l}from"node:url";import{readFile as u}from"fs/promises";import{createReadStream as d}from"fs";import{envConfig as v}from"./config/env-config.js";import{telemetry as g}from"./telemetry/index.js";import{createRouter as S}from"./web-server/router.js";import{installProdRoutes as E}from"./web-server/routes/index.js";import{Store as w}from"./store.js";import{loadEnvVariables as D}from"./utils/envs/load-env-variables.js";import{startHttpServer as P}from"./web-server/http.js";import{readStaticData as x}from"./utils/static-data.js";import{startIdleTimeout as R}from"./web-server/middleware/idleTimeoutMiddleware.js";import{reporter as b}from"./tools/notifiers/reporter.js";import{EntitlementsProvider as y}from"./entitlements/entitlements-provider.js";import{DatabasePreconnectService as I}from"./providers/database/database-preconnect-service.js";import{KvService as h}from"./persistence/kv/services/kv-service.js";import{runScorecardsWorker as N}from"./plugins/scorecards/workers/run-scorecards-worker.js";import{isCatalogEntitiesEnabled as O}from"./utils/is-catalog-entities-enabled.js";import{isScorecardsEnabled as T}from"./utils/is-scorecards-enabled.js";import{renderPage as tr}from"./ssr/index.js";const k=new URL(import.meta.url),A=k.searchParams.get("only-exports")==="true";if(!A){let p=function(){const r=process.argv.findIndex(i=>i==="--port"||i==="-p");if(r===-1)return null;const o=process.argv[r+1];if(o.startsWith("-"))return null;const e=parseInt(o,10);return isNaN(e)?null:e};const t=f(l(import.meta.url));await D(t);const s=JSON.parse(await u(n(t,"./store.json"),"utf-8")),m=w.fromJson(s,{outdir:n(t,"../client"),serverOutDir:t,serverMode:!0,contentDir:""});if(await y.instance().init({ignoreTokenExpiration:!0}),O()){await I.init(t);const r=await h.getInstance({baseDbDir:t});setInterval(()=>r.clearExpired(),300*1e3)}c(m).catch(r=>{console.error(r)});async function c(r){g.initialize();const o=await S();E(o,r,{readStaticAsset:async a=>d(a),resolveRouteData:async a=>x(a.slug,r.outdir)}),b.printErrors();const e=p(),i=v.PORT;return await P(o,e??i??4e3),R(),T(r.config)&&N(r.serverOutDir,r.config.scorecards),o}}export{tr as renderPage};
1
+ import{resolve as n,dirname as f}from"node:path";import{fileURLToPath as l}from"node:url";import{readFile as u}from"fs/promises";import{createReadStream as d}from"fs";import{envConfig as v}from"./config/env-config.js";import{telemetry as g}from"./telemetry/index.js";import{createRouter as S}from"./web-server/router.js";import{installProdRoutes as R}from"./web-server/routes/index.js";import{Store as w}from"./store.js";import{loadEnvVariables as x}from"./utils/envs/load-env-variables.js";import{startHttpServer as D}from"./web-server/http.js";import{readStaticData as P}from"./utils/static-data.js";import{startIdleTimeout as E}from"./web-server/middleware/idleTimeoutMiddleware.js";import{reporter as y}from"./tools/notifiers/reporter.js";import{logger as b}from"./tools/notifiers/logger.js";import{EntitlementsProvider as h}from"./entitlements/entitlements-provider.js";import{DatabasePreconnectService as I}from"./providers/database/database-preconnect-service.js";import{KvService as O}from"./persistence/kv/services/kv-service.js";import{runScorecardsWorker as N}from"./plugins/scorecards/workers/run-scorecards-worker.js";import{isScorecardsEnabled as T}from"./utils/is-scorecards-enabled.js";import{isRealmOrReef as k}from"./utils/is-realm-or-reef.js";import{renderPage as or}from"./ssr/index.js";const A=new URL(import.meta.url),F=A.searchParams.get("only-exports")==="true";if(!F){let p=function(){const r=process.argv.findIndex(i=>i==="--port"||i==="-p");if(r===-1)return null;const t=process.argv[r+1];if(t.startsWith("-"))return null;const o=parseInt(t,10);return isNaN(o)?null:o};const e=f(l(import.meta.url));await x(e);const s=JSON.parse(await u(n(e,"./store.json"),"utf-8")),m=w.fromJson(s,{outdir:n(e,"../client"),serverOutDir:e,serverMode:!0,contentDir:""});if(await h.instance().init({ignoreTokenExpiration:!0}),k()){await I.init(e);const r=await O.getInstance({baseDbDir:e});setInterval(()=>{r.clearExpired().catch(t=>{b.error("Failed to clear expired KV entries",t)})},300*1e3)}c(m).catch(r=>{console.error(r)});async function c(r){g.initialize();const t=await S();R(t,r,{readStaticAsset:async a=>d(a),resolveRouteData:async a=>P(a.slug,r.outdir)}),y.printErrors();const o=p(),i=v.PORT;return await D(t,o??i??4e3),E(),T(r.config)&&N(r.serverOutDir,r.config.scorecards),t}}export{or as renderPage};
@@ -1 +1 @@
1
- import{and as l,eq as a}from"drizzle-orm";import{isTtlExpired as d}from"../../../utils/is-ttl-expired.js";import{logger as o}from"../../../tools/notifiers/logger.js";import{DatabaseConnectionFactory as m}from"../../../providers/database/database-connection-factory.js";import{cacheTable as r}from"../../../providers/database/databases/sqlite-db/schemas/cache-table.js";import{createCacheDbRecord as u}from"../mappers/create-cache-db-record.js";import{createCacheReadModel as h}from"../mappers/create-cache-read-model.js";class c{static#t;#e;constructor(t){this.#e=t.client}static async getInstance(t){if(!c.#t){const e=await m.create(t);if(!e)throw new Error("Failed to create db connection for cache repository");c.#t=new c(e)}return c.#t}async get(t,e){const n=[a(r.key,t)];e&&n.push(a(r.namespace,e));const i=await this.#e.client.select().from(r).where(l(...n)).get();if(!i)return null;const s=h(i);return d(s.ttlInSeconds,s.createdAt)?(await this.delete(t,e),null):s}async set(t){try{const e=u(t);return await this.#e.client.insert(r).values(e).onConflictDoUpdate({target:[r.key],set:{value:e.value,namespace:e.namespace,ttl:e.ttl,updatedAt:e.updatedAt}}),e.key}catch(e){return o.error("Error setting cache: "+e.message),null}}async delete(t,e){try{return await this.#e.client.delete(r).where(l(a(r.key,t),a(r.namespace,e))),!0}catch(n){return o.error("Error deleting cache: "+n.message),!1}}async deleteByNamespace(t){try{return await this.#e.client.delete(r).where(a(r.namespace,t)),!0}catch(e){return o.error("Error deleting cache by namespace: "+e.message),!1}}async clearAll(){try{return await this.#e.client.delete(r),!0}catch(t){return o.error("Error clearing all cache: "+t.message),!1}}}export{c as CacheRepository};
1
+ import{and as i,eq as a}from"drizzle-orm";import{isTtlExpired as m}from"../../../utils/is-ttl-expired.js";import{logger as n}from"../../../tools/notifiers/logger.js";import{cacheTable as r}from"../../../providers/database/databases/sqlite-db/schemas/cache-table.js";import{createDatabaseRepositoryInstance as d,InstanceCacheResolver as h}from"../../../providers/database/instance-cache-resolver.js";import{createCacheDbRecord as u}from"../mappers/create-cache-db-record.js";import{createCacheReadModel as p}from"../mappers/create-cache-read-model.js";class l{static#t=new h(e=>d(e,"cache",l));#e;constructor(e){this.#e=e.client}static async getInstance(e){return l.#t.get(e)}async get(e,t){const c=[a(r.key,e)];t&&c.push(a(r.namespace,t));const o=await this.#e.client.select().from(r).where(i(...c)).get();if(!o)return null;const s=p(o);return m(s.ttlInSeconds,s.createdAt)?(await this.delete(e,t),null):s}async set(e){try{const t=u(e);return await this.#e.client.insert(r).values(t).onConflictDoUpdate({target:[r.key],set:{value:t.value,namespace:t.namespace,ttl:t.ttl,updatedAt:t.updatedAt}}),t.key}catch(t){return n.error("Error setting cache: "+t.message),null}}async delete(e,t){try{return await this.#e.client.delete(r).where(i(a(r.key,e),a(r.namespace,t))),!0}catch(c){return n.error("Error deleting cache: "+c.message),!1}}async deleteByNamespace(e){try{return await this.#e.client.delete(r).where(a(r.namespace,e)),!0}catch(t){return n.error("Error deleting cache by namespace: "+t.message),!1}}async clearAll(){try{return await this.#e.client.delete(r),!0}catch(e){return n.error("Error clearing all cache: "+e.message),!1}}}export{l as CacheRepository};
@@ -1 +1 @@
1
- import{DatabaseConnectionFactory as a}from"../../../providers/database/database-connection-factory.js";import{BaseRepository as r}from"../../../providers/database/base-repository.js";import{FileHashesWriteRepository as i}from"./file-hashes-write-repository.js";import{FileHashesReadRepository as n}from"./file-hashes-read-repository.js";class e extends r{static#s;#t;#e;constructor(t){super(t),this.#t=new n(this.databaseClient),this.#e=new i(this.databaseClient)}static async getInstance(t){if(!e.#s){const s=await a.create(t);if(!s)throw new Error("Failed to create db connection for file hashes repository");e.#s=new e(s)}return e.#s}async getByPath(t){return this.#t.getByPath(t)}async getAllOutdated(t){return this.#t.getAllOutdated(t)}async getAllByFileType(t){return this.#t.getAllByFileType(t)}async upsertFileHash(t){return await this.#e.upsertFileHash(t)}async updateFileHashesStatus(t,s){return this.#e.updateFileHashes(t,s)}async deleteFileHashes(t){return this.#e.deleteFileHashes(t)}}export{e as FileHashesRepository};
1
+ import{BaseRepository as a}from"../../../providers/database/base-repository.js";import{createDatabaseRepositoryInstance as r,InstanceCacheResolver as i}from"../../../providers/database/instance-cache-resolver.js";import{FileHashesWriteRepository as n}from"./file-hashes-write-repository.js";import{FileHashesReadRepository as l}from"./file-hashes-read-repository.js";class t extends a{static#s=new i(e=>r(e,"file hashes",t));#e;#t;constructor(e){super(e),this.#e=new l(this.databaseClient),this.#t=new n(this.databaseClient)}static async getInstance(e){return t.#s.get(e)}async getByPath(e){return this.#e.getByPath(e)}async getAllOutdated(e){return this.#e.getAllOutdated(e)}async getAllByFileType(e){return this.#e.getAllByFileType(e)}async upsertFileHash(e){return await this.#t.upsertFileHash(e)}async updateFileHashesStatus(e,s){return this.#t.updateFileHashes(e,s)}async deleteFileHashes(e){return this.#t.deleteFileHashes(e)}}export{t as FileHashesRepository};
@@ -5,7 +5,7 @@ export declare const KV_KEY_END_BOUNDARY = "\u0002";
5
5
  export declare class KvRepository extends BaseRepository {
6
6
  #private;
7
7
  constructor(dbConnection: DatabaseConnection);
8
- static getInstance(options: RepositoryInstanceOptions): Promise<KvRepository | null>;
8
+ static create(options: RepositoryInstanceOptions): Promise<KvRepository>;
9
9
  sync(): Promise<void>;
10
10
  get<T extends KvValue = KvValue>(key: KvKey): Promise<T | null>;
11
11
  getMany<T extends KvValue = KvValue>(keys: KvKey[]): Promise<(KvListEntry<T> | null)[]>;
@@ -1,2 +1,2 @@
1
- import{eq as K,and as h,gte as u,gt as B,lt as f,asc as S,desc as L,or as w,isNull as b,sql as s,inArray as M,count as N}from"drizzle-orm";import{logger as i}from"../../../tools/notifiers/logger.js";import{kvTable as t}from"../../../providers/database/databases/sqlite-db/schemas/kv-table.js";import{BaseRepository as T}from"../../../providers/database/base-repository.js";import{DatabaseConnectionFactory as $}from"../../../providers/database/database-connection-factory.js";import{createKvValue as D}from"../mappers/create-kv-value.js";import{createKvDbRecord as I,encodeKvKey as l}from"../mappers/create-kv-db-record.js";import{decodeCursor as O}from"../helpers/decode-cursor.js";import{encodeCursor as q}from"../helpers/encode-cursor.js";import{createKvListEntry as E}from"../mappers/create-kv-list-entry.js";const z="";class y extends T{static#e;#t=!1;constructor(e){super(e)}static async getInstance(e){if(!y.#e)try{const r=await $.create(e);if(!r)return y.#e=null,null;y.#e=new y(r)}catch(r){return i.error("Error creating kv remote repository",r),y.#e=null,null}return y.#e}async sync(){if(this.isNonRemoteDatabaseMode()){this.#t||(i.warn(`KV database is currently operating in local mode: not connected to the remote database.
2
- All changes and data will only persist locally and will not be synced remotely.`),this.#t=!0);return}await this.databaseClient.sync()}async get(e){try{const r=l(e),n=await this.databaseClient.client.select().from(t).where(h(K(t.encodedKey,r),w(b(t.expiresAt),u(s`datetime(${t.expiresAt})`,s`datetime('now')`)))).get();return n?D(n):null}catch(r){return i.error("Error getting kv entry by key",r),null}}async getMany(e){try{if(e.length===0)return[];const r=e.map(d=>l(d)),n=w(b(t.expiresAt),u(s`datetime(${t.expiresAt})`,s`datetime('now')`)),o=await this.databaseClient.client.select().from(t).where(h(M(t.encodedKey,r),n)).all(),a=new Map(o.map(d=>[d.encodedKey,d]));return e.map((d,C)=>{const g=r[C],x=a.get(g);return x?E(x):null})}catch(r){return i.error("Error getting multiple kv entries",r),[]}}async list(e,r){try{const n=r?.limit??100,o=r?.reverse??!1,a=[],A=w(b(t.expiresAt),u(s`datetime(${t.expiresAt})`,s`datetime('now')`));if(a.push(A),"prefix"in e){const c=l(e.prefix),m="start"in e?l(e.start):c,k="end"in e?l(e.end):c+z;a.push(u(t.encodedKey,m)),a.push(f(t.encodedKey,k))}else if("start"in e&&"end"in e){const c=l(e.start),m=l(e.end);a.push(u(t.encodedKey,c)),a.push(f(t.encodedKey,m))}if(r?.cursor){const c=O(r.cursor),m=o?f(t.encodedKey,c):B(t.encodedKey,c);a.push(m)}const d=this.databaseClient.client.select().from(t),C=a.length>0?d.where(h(...a)):d,g=this.databaseClient.client.select({count:N()}).from(t),v=(await(a.length>0?g.where(h(...a)):g).get())?.count??0,p=await C.orderBy(o?L(t.encodedKey):S(t.encodedKey)).limit(n).all();return{items:p.map(c=>E(c)),total:v,cursor:v>p.length?q(p[p.length-1]?.encodedKey):null}}catch(n){return i.error("Error listing kv entries",n),{items:[],total:0,cursor:null}}}async set(e,r,n){try{const o=I({key:e,value:r,ttlInSeconds:n?.ttlInSeconds});return await this.databaseClient.client.insert(t).values(o).onConflictDoUpdate({target:[t.encodedKey],set:{value:o.value,expiresAt:o.expiresAt,updatedAt:o.updatedAt}}),E(o)}catch(o){return i.error("Error saving kv entry",o),null}}async delete(e){try{const r=l(e);await this.databaseClient.client.delete(t).where(K(t.encodedKey,r))}catch(r){i.error("Error deleting kv entry by key",r)}}async clearExpired(){try{await this.databaseClient.client.delete(t).where(f(s`datetime(${t.expiresAt})`,s`datetime('now')`))}catch(e){i.error("Error clearing expired kv entries",e)}}async transaction(e){return this.databaseClient.transactionsManager.transaction(async()=>e({get:async n=>this.get(n),getMany:async n=>this.getMany(n),set:async(n,o,a)=>this.set(n,o,a),delete:async n=>this.delete(n)}))}async getTotalStoredEntryBytes(){try{return await this.getTableSizeInBytes("kv")}catch(e){return i.error("Error getting total kv stored entry bytes",e),0}}async getStoredEntrySizeByEncodedKey(e){try{const r=w(b(t.expiresAt),u(s`datetime(${t.expiresAt})`,s`datetime('now')`)),n=await this.databaseClient.client.select({bytes:s`COALESCE(LENGTH(CAST(${t.encodedKey} AS BLOB)) + LENGTH(CAST(${t.value} AS BLOB)), 0)`}).from(t).where(h(K(t.encodedKey,e),r)).get();return Number(n?.bytes??0)}catch(r){return i.error("Error getting kv entry size by encoded key",r),0}}}export{z as KV_KEY_END_BOUNDARY,y as KvRepository};
1
+ import{eq as b,and as m,gte as l,gt as B,lt as g,asc as S,desc as R,or as f,isNull as K,sql as o,inArray as L,count as M}from"drizzle-orm";import{logger as C}from"../../../tools/notifiers/logger.js";import{kvTable as e}from"../../../providers/database/databases/sqlite-db/schemas/kv-table.js";import{BaseRepository as N}from"../../../providers/database/base-repository.js";import{createDatabaseRepositoryInstance as T}from"../../../providers/database/instance-cache-resolver.js";import{createKvValue as $}from"../mappers/create-kv-value.js";import{createKvDbRecord as D,encodeKvKey as c}from"../mappers/create-kv-db-record.js";import{decodeCursor as k}from"../helpers/decode-cursor.js";import{encodeCursor as I}from"../helpers/encode-cursor.js";import{createKvListEntry as x}from"../mappers/create-kv-list-entry.js";const O="";class v extends N{#e=!1;constructor(t){super(t)}static create(t){return T(t,"kv",v)}async sync(){if(this.isNonRemoteDatabaseMode()){this.#e||(C.warn(`KV database is currently operating in local mode: not connected to the remote database.
2
+ All changes and data will only persist locally and will not be synced remotely.`),this.#e=!0);return}await this.databaseClient.sync()}async get(t){const n=c(t),r=await this.databaseClient.client.select().from(e).where(m(b(e.encodedKey,n),f(K(e.expiresAt),l(o`datetime(${e.expiresAt})`,o`datetime('now')`)))).get();return r?$(r):null}async getMany(t){if(t.length===0)return[];const n=t.map(d=>c(d)),r=f(K(e.expiresAt),l(o`datetime(${e.expiresAt})`,o`datetime('now')`)),a=await this.databaseClient.client.select().from(e).where(m(L(e.encodedKey,n),r)).all(),s=new Map(a.map(d=>[d.encodedKey,d]));return t.map((d,p)=>{const w=n[p],y=s.get(w);return y?x(y):null})}async list(t,n){const r=n?.limit??100,a=n?.reverse??!1,s=[],d=f(K(e.expiresAt),l(o`datetime(${e.expiresAt})`,o`datetime('now')`));if(s.push(d),"prefix"in t){const i=c(t.prefix),u="start"in t?c(t.start):i,E="end"in t?c(t.end):i+O;s.push(l(e.encodedKey,u)),s.push(g(e.encodedKey,E))}else if("start"in t&&"end"in t){const i=c(t.start),u=c(t.end);s.push(l(e.encodedKey,i)),s.push(g(e.encodedKey,u))}if(n?.cursor){const i=k(n.cursor),u=a?g(e.encodedKey,i):B(e.encodedKey,i);s.push(u)}const p=this.databaseClient.client.select().from(e),w=s.length>0?p.where(m(...s)):p,y=this.databaseClient.client.select({count:M()}).from(e),A=(await(s.length>0?y.where(m(...s)):y).get())?.count??0,h=await w.orderBy(a?R(e.encodedKey):S(e.encodedKey)).limit(r).all();return{items:h.map(i=>x(i)),total:A,cursor:A>h.length?I(h[h.length-1]?.encodedKey):null}}async set(t,n,r){const a=D({key:t,value:n,ttlInSeconds:r?.ttlInSeconds});return await this.databaseClient.client.insert(e).values(a).onConflictDoUpdate({target:[e.encodedKey],set:{value:a.value,expiresAt:a.expiresAt,updatedAt:a.updatedAt}}),x(a)}async delete(t){const n=c(t);await this.databaseClient.client.delete(e).where(b(e.encodedKey,n))}async clearExpired(){await this.databaseClient.client.delete(e).where(g(o`datetime(${e.expiresAt})`,o`datetime('now')`))}async transaction(t){return this.databaseClient.transactionsManager.transaction(async()=>t({get:async r=>this.get(r),getMany:async r=>this.getMany(r),set:async(r,a,s)=>this.set(r,a,s),delete:async r=>this.delete(r)}))}async getTotalStoredEntryBytes(){try{return await this.getTableSizeInBytes("kv")}catch(t){return C.error("Error getting total kv stored entry bytes",t),0}}async getStoredEntrySizeByEncodedKey(t){try{const n=f(K(e.expiresAt),l(o`datetime(${e.expiresAt})`,o`datetime('now')`)),r=await this.databaseClient.client.select({bytes:o`COALESCE(LENGTH(CAST(${e.encodedKey} AS BLOB)) + LENGTH(CAST(${e.value} AS BLOB)), 0)`}).from(e).where(m(b(e.encodedKey,t),n)).get();return Number(r?.bytes??0)}catch(n){return C.error("Error getting kv entry size by encoded key",n),0}}}export{O as KV_KEY_END_BOUNDARY,v as KvRepository};
@@ -1,9 +1,10 @@
1
1
  import type { KvValue, KvKey, KvListEntry, KvSetOptions, KvListOptions, KvListResponse, KvListSelector, KvTransaction } from '@redocly/config';
2
2
  import type { ServiceInstanceOptions } from '../../../providers/database/types.js';
3
+ import { BaseService } from '../../../providers/database/base-service.js';
3
4
  import { KvRepository } from '../repositories/kv-repository.js';
4
- export declare class KvService {
5
+ export declare class KvService extends BaseService<KvRepository> {
5
6
  #private;
6
- constructor(repository: KvRepository | null);
7
+ constructor(repository: KvRepository, options?: ServiceInstanceOptions);
7
8
  static getInstance(options: ServiceInstanceOptions): Promise<KvService>;
8
9
  /**
9
10
  * Get a kv entry by key
@@ -1 +1 @@
1
- import{PLAN_GATES_DEFAULTS as u}from"../../../constants/entitlements.js";import{EntitlementsProvider as d}from"../../../entitlements/entitlements-provider.js";import{StorageLimitValidator as h}from"../../../providers/database/utils/storage-limit-validator.js";import{KvRepository as m}from"../repositories/kv-repository.js";import{encodeKvKey as g}from"../mappers/create-kv-db-record.js";import{kvKeyValidator as a,kvListOptionsValidator as f,kvListSelectorValidator as w,kvSetOptionsValidator as l}from"../schemas/kv-schemas.js";const E=100*1024*1024;class o{static#e;#t;#s;constructor(t){this.#t=t,this.#s=new h}static async#a(t){const e=await m.getInstance(t),s=new o(e);o.#e=s}static async getInstance(t){return o.#e||await o.#a(t),o.#e}async get(t){const e=a.parse(t);return await this.#t?.sync(),await this.#t?.get(e)??null}async getMany(t){if(t.length===0)return[];const e=t.map(s=>a.parse(s));return await this.#t?.sync(),await this.#t?.getMany(e)??[]}async list(t,e){const s=w.parse(t),r=f.parse(e??{});return await this.#t?.sync(),await this.#t?.list(s,r)??{items:[],total:0,cursor:null}}async set(t,e,s){const r=a.parse(t),n=l.parse(s??{});this.#r(e);const i=JSON.stringify(e);return this.#n(i),await this.#i(r,i),await this.#t?.set(r,e,n)??null}async delete(t){const e=a.parse(t);return this.#t?.delete(e)}async clearExpired(){return await this.#t?.clearExpired()}async transaction(t){if(!this.#t)throw new Error("Remote repository not available for transactions");return await this.#t.sync(),this.#t.transaction(async e=>t({get:async r=>{const n=a.parse(r);return e.get(n)},getMany:async r=>{const n=r.map(i=>a.parse(i));return e.getMany(n)},set:async(r,n,i)=>{const c=a.parse(r),p=l.parse(i??{});this.#r(n);const y=JSON.stringify(n);return this.#n(y),await this.#i(c,y),e.set(c,n,p)},delete:async r=>{const n=a.parse(r);return e.delete(n)}}))}#r(t){try{JSON.stringify(t)}catch(e){const s=e instanceof Error?e.message:"Unknown error";throw new Error(`Value is not JSON serializable: ${s}`)}}#n(t){const s=Buffer.byteLength(t,"utf8");if(s>1048576){const r=(s/1024).toFixed(2);throw new Error(`Value size (${r} KB) exceeds the maximum allowed size of 1 MB (1024 KB)`)}}async#i(t,e){if(!this.#t)return;const s=g(t),r=await this.#t.getTotalStoredEntryBytes(),n=Math.max(0,r-E),i=await this.#t.getStoredEntrySizeByEncodedKey(s),c=Buffer.byteLength(s,"utf8")+Buffer.byteLength(e,"utf8");this.#s.validate({storageLimitGb:this.#o(),currentTotalBytes:n,existingEntryBytes:i,incomingEntryBytes:c,errorMessagePrefix:"KV storage limit"})}#o(){const t=d.instance().entitlements?.kvStorageLimit;return typeof t=="number"&&t>0?t:u.pro.kvStorageLimit}}export{o as KvService};
1
+ import{PLAN_GATES_DEFAULTS as g}from"../../../constants/entitlements.js";import{EntitlementsProvider as m}from"../../../entitlements/entitlements-provider.js";import{BaseService as S}from"../../../providers/database/base-service.js";import{InstanceCacheResolver as f}from"../../../providers/database/instance-cache-resolver.js";import{StorageLimitValidator as h}from"../../../providers/database/utils/storage-limit-validator.js";import{KvRepository as p}from"../repositories/kv-repository.js";import{encodeKvKey as E}from"../mappers/create-kv-db-record.js";import{kvKeyValidator as i,kvListOptionsValidator as B,kvListSelectorValidator as w,kvSetOptionsValidator as d}from"../schemas/kv-schemas.js";const k=100*1024*1024;class l extends S{static#n=new f(async t=>new l(await p.create(t),t));#t;constructor(t,e){super(e,{repository:t,createRepository:p.create}),this.#t=new h}static async getInstance(t){return l.#n.get(t)}async get(t){const e=i.parse(t);return this.runRead(r=>r.get(e))}async getMany(t){if(t.length===0)return[];const e=t.map(r=>i.parse(r));return this.runRead(r=>r.getMany(e))}async list(t,e){const r=w.parse(t),s=B.parse(e??{});return this.runRead(n=>n.list(r,s))}async set(t,e,r){const s=i.parse(t),n=d.parse(r??{});this.#e(e);const a=JSON.stringify(e);return this.#r(a),await this.#s(s,a),this.runWrite(o=>o.set(s,e,n))}async delete(t){const e=i.parse(t);await this.runWrite(r=>r.delete(e))}async clearExpired(){await this.runWrite(t=>t.clearExpired())}async transaction(t){return await this.syncRepositoryIfNeeded(),this.getRepository().transaction(async e=>t({get:async s=>{const n=i.parse(s);return e.get(n)},getMany:async s=>{const n=s.map(a=>i.parse(a));return e.getMany(n)},set:async(s,n,a)=>{const o=i.parse(s),c=d.parse(a??{});this.#e(n);const y=JSON.stringify(n);return this.#r(y),await this.#s(o,y,{skipSync:!0}),e.set(o,n,c)},delete:async s=>{const n=i.parse(s);return e.delete(n)}}))}#e(t){try{JSON.stringify(t)}catch(e){const r=e instanceof Error?e.message:"Unknown error";throw new Error(`Value is not JSON serializable: ${r}`)}}#r(t){const r=Buffer.byteLength(t,"utf8");if(r>1048576){const s=(r/1024).toFixed(2);throw new Error(`Value size (${s} KB) exceeds the maximum allowed size of 1 MB (1024 KB)`)}}async#s(t,e,r){const s=E(t),n=await this.runRead(async c=>{const y=await c.getTotalStoredEntryBytes(),u=await c.getStoredEntrySizeByEncodedKey(s);return{currentTotalStoredEntryBytes:y,existingEntryBytes:u}},{onRepositoryMissing:()=>null,skipSync:r?.skipSync??!1});if(!n)return;const a=Math.max(0,n.currentTotalStoredEntryBytes-k),o=Buffer.byteLength(s,"utf8")+Buffer.byteLength(e,"utf8");this.#t.validate({storageLimitGb:this.#a(),currentTotalBytes:a,existingEntryBytes:n.existingEntryBytes,incomingEntryBytes:o,errorMessagePrefix:"KV storage limit"})}#a(){const t=m.instance().entitlements?.kvStorageLimit;return typeof t=="number"&&t>0?t:g.pro.kvStorageLimit}}export{l as KvService};
@@ -8,10 +8,11 @@ import type { Filter } from '../../../providers/database/pagination/types.js';
8
8
  import type { ScorecardsStatus } from '../entities/types.js';
9
9
  import type { GetEntityByIdParams } from '../types/params.js';
10
10
  import type { BulkSyncResult } from './types.js';
11
+ import { BaseService } from '../../../providers/database/base-service.js';
11
12
  import { type EntityDtoSchema, type EntityRelationDtoSchema } from '../schemas/dto-schemas.js';
12
13
  import { CatalogEntitiesRepository } from './repositories/catalog-entities-repository.js';
13
14
  import { type ListResponseResult } from '../../../web-server/utils/prepare-list-response.js';
14
- export declare class CatalogEntitiesService {
15
+ export declare class CatalogEntitiesService extends BaseService<CatalogEntitiesRepository> {
15
16
  #private;
16
17
  constructor(catalogEntitiesRepository: CatalogEntitiesRepository, options?: ServiceInstanceOptions);
17
18
  static getInstance(options: ServiceInstanceOptions): Promise<CatalogEntitiesService>;
@@ -1 +1 @@
1
- import{promiseMapLimit as h}from"../../../utils/async/promise-map-limit.js";import{logger as g}from"../../../tools/notifiers/logger.js";import{envConfig as y}from"../../../config/env-config.js";import{HTTP_TRANSIENT_RETRY as l,describeTransientError as m,isTransientSqldError as w,withTransientErrorRetry as d}from"../../../providers/database/transient-sqld-error.js";import{CatalogEntitiesRepository as E}from"./repositories/catalog-entities-repository.js";import{prepareListResponse as u}from"../../../web-server/utils/prepare-list-response.js";import{ENTITY_RELATION_FROM_DATABASE as R}from"./mappers/field-transformations.js";import{hasOptionsChanged as W}from"../utils/has-options-changed.js";const p=15;class s{static#s;static#n;#i;#r;#a={};constructor(t,e){this.#i=t,this.#r=e?.databaseType==="local"}static async#c(t){const e=await E.getInstance(t),i=new s(e,t);return s.#s=i,s.#n=t,i}static async getInstance(t){const e={...t,removeExisting:t.removeExisting??!1};return s.#s&&!W(s.#n,e)?s.#s:s.#c(e)}async sync(){await this.#o()}async transaction(...t){return this.#i.transactionsManager.transaction(...t)}async getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n}){return this.#e(async r=>{const{items:a,total:o,hasMore:c}=await r.entitiesRead.getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n});return u({data:a,params:t,totalCount:o,hasMore:c})})}async getEntityById(t,e){return this.#e(async i=>await i.entitiesRead.getEntityById(t,e)??null)}async getEntityKeysAndVersionsBySourceFile(t){return this.#e(e=>e.entitiesRead.getEntityKeysAndVersionsBySourceFile(t))}async getEntitiesCountByTypes(){return this.#e(t=>t.entitiesRead.getEntitiesCountByTypes())}async getCatalogFilters(t){return this.#e(e=>e.filters.getCatalogFilters(t))}async getEntityRelationById(t){return this.#e(async e=>await e.relationsRead.getEntityRelationById(t)??null)}async getEntitiesRelations(t={}){return this.#e(async e=>{const{items:i,total:n,hasMore:r}=await e.relationsRead.getEntitiesRelations(t);return u({data:i,params:t,totalCount:n,nameTransformationsFromDatabase:R,hasMore:r})})}async getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n}){return this.#e(async r=>{const{items:a,total:o,hasMore:c}=await r.bffEntitiesRead.getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n});return u({data:a,params:t,totalCount:o,hasMore:c})})}async getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:n,excludedEntities:r}){return this.#e(a=>a.bffEntitiesRead.getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:n,excludedEntities:r}))}async getRelatedEntities({entityKey:t,paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:r}){return this.#e(async a=>{const{items:o,total:c,hasMore:f}=await a.relationsRead.getRelatedEntities({key:t,paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:r});return u({data:o,params:e,totalCount:c,nameTransformationsFromDatabase:R,hasMore:f})})}async listEntityRevisions(t,e){return this.#e(i=>i.revisions.listEntityRevisions(t,e))}async getOutdatedEntities(t){return this.#e(e=>e.entitiesRead.getOutdatedEntities(t))}async getEntitiesCount(t,e,i){return this.#e(n=>n.entitiesRead.getEntitiesCount(t,e,i))}async createEntity(t){if(y.isDevelopMode&&t.source==="remote")throw new Error("API based entity creation is not supported in the develop mode");return t.isRootEntity&&t.sourceFile&&(this.#a[t.sourceFile]={key:t.entity.key,version:t.entity.version??void 0}),this.#t(e=>e.entitiesWrite.createEntity(t))}async createEntities(t,e){if(y.isDevelopMode&&e==="remote")throw new Error("API based entity creation is not supported in the develop mode");return await h(t,p,async i=>this.#t(n=>n.entitiesWrite.createEntity({entity:i,source:e})).then(n=>({status:"ok",resource:n})).catch(n=>({key:i.key,status:"error",error:n})))}getEntitySources(){return this.#a}async createEntityRelations(t){await this.#t(e=>e.relationsWrite.createEntityRelations(t))}async updateEntity(t,e){if(y.isDevelopMode&&e.source==="remote")throw new Error("Entity update is not supported in the develop mode");return this.#t(i=>i.transactionsManager.transaction(()=>i.entitiesWrite.updateEntity(t,e)))}async deleteEntity(t){if(y.isDevelopMode&&t.source==="remote")throw new Error("Entity deletion is not supported in the develop mode");return this.#t(e=>e.entitiesWrite.deleteEntity(t))}async deleteEntities(t){await this.#t(async e=>{await e.entitiesWrite.deleteEntities(t)})}async createEntityRelation(t){return this.#t(e=>e.relationsWrite.createEntityRelation(t))}async createEntitiesRelations(t){return await h(t,p,async e=>this.#t(i=>i.relationsWrite.createEntityRelation(e)).then(i=>({status:"ok",resource:i})).catch(i=>({key:e.sourceKey,status:"error",error:i})))}async updateEntityRelation(t,e){const i={...e,...t,type:t.type??e.type};return this.#t(n=>n.relationsWrite.createEntityRelation(i))}async deleteEntityRelation(t){return this.#t(e=>e.relationsWrite.deleteEntityRelation(t))}async softDeleteEntitiesWithRelations({filter:t,revision:e,fileHash:i}){return this.#t(n=>n.entitiesWrite.softDeleteEntitiesWithRelations({filter:t,revision:e,fileHash:i}))}async updateEntityScorecardsStatus(t,e){return this.#t(i=>i.entitiesWrite.updateEntityScorecardsStatus(t,e))}async updateEntityScorecardsStatusIfCalculating(t,e){return this.#t(i=>i.entitiesWrite.updateEntityScorecardsStatusIfCalculating(t,e))}async setEntitiesAsOutdated(t){await this.#t(async e=>{await e.entitiesWrite.setEntitiesAsOutdated(t)})}async#o(){if(this.#r)return;const t=this.#i;await d(()=>t.sync(),l)}async#e(t){return await this.#o(),d(()=>t(this.#i),l)}async#t(t){return this.#r?t(this.#i):d(async()=>{try{return await t(this.#i)}catch(e){throw await this.#y(e),e}},l)}async#y(t){if(!(!w(t)||!s.#n))try{this.#i=await E.recreateInstance(s.#n)}catch(e){g.warn(`Reconnect attempt failed, letting original error propagate: ${m(e)}`)}}}export{s as CatalogEntitiesService};
1
+ import{promiseMapLimit as d}from"../../../utils/async/promise-map-limit.js";import{envConfig as u}from"../../../config/env-config.js";import{BaseService as p}from"../../../providers/database/base-service.js";import{InstanceCacheResolver as W}from"../../../providers/database/instance-cache-resolver.js";import{CatalogEntitiesRepository as l}from"./repositories/catalog-entities-repository.js";import{prepareListResponse as y}from"../../../web-server/utils/prepare-list-response.js";import{ENTITY_RELATION_FROM_DATABASE as E}from"./mappers/field-transformations.js";const R=15;class c extends p{static#e=new W(async t=>new c(await l.create(t),t));#t={};constructor(t,e){super(e,{repository:t,createRepository:l.create})}static async getInstance(t){return c.#e.get(t)}async sync(){await this.syncRepositoryIfNeeded()}async transaction(...t){return this.getRepository().transactionsManager.transaction(...t)}async getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n}){return this.runRead(async s=>{const{items:r,total:a,hasMore:o}=await s.entitiesRead.getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n});return y({data:r,params:t,totalCount:a,hasMore:o})})}async getEntityById(t,e){return this.runRead(async i=>await i.entitiesRead.getEntityById(t,e)??null)}async getEntityKeysAndVersionsBySourceFile(t){return this.runRead(e=>e.entitiesRead.getEntityKeysAndVersionsBySourceFile(t))}async getEntitiesCountByTypes(){return this.runRead(t=>t.entitiesRead.getEntitiesCountByTypes())}async getCatalogFilters(t){return this.runRead(e=>e.filters.getCatalogFilters(t))}async getEntityRelationById(t){return this.runRead(async e=>await e.relationsRead.getEntityRelationById(t)??null)}async getEntitiesRelations(t={}){return this.runRead(async e=>{const{items:i,total:n,hasMore:s}=await e.relationsRead.getEntitiesRelations(t);return y({data:i,params:t,totalCount:n,nameTransformationsFromDatabase:E,hasMore:s})})}async getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n}){return this.runRead(async s=>{const{items:r,total:a,hasMore:o}=await s.bffEntitiesRead.getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:n});return y({data:r,params:t,totalCount:a,hasMore:o})})}async getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:n,excludedEntities:s}){return this.runRead(r=>r.bffEntitiesRead.getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:n,excludedEntities:s}))}async getRelatedEntities({entityKey:t,paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:s}){return this.runRead(async r=>{const{items:a,total:o,hasMore:h}=await r.relationsRead.getRelatedEntities({key:t,paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:s});return y({data:a,params:e,totalCount:o,nameTransformationsFromDatabase:E,hasMore:h})})}async listEntityRevisions(t,e){return this.runRead(i=>i.revisions.listEntityRevisions(t,e))}async getOutdatedEntities(t){return this.runRead(e=>e.entitiesRead.getOutdatedEntities(t))}async getEntitiesCount(t,e,i){return this.runRead(n=>n.entitiesRead.getEntitiesCount(t,e,i))}async createEntity(t){if(u.isDevelopMode&&t.source==="remote")throw new Error("API based entity creation is not supported in the develop mode");return t.isRootEntity&&t.sourceFile&&(this.#t[t.sourceFile]={key:t.entity.key,version:t.entity.version??void 0}),this.runWrite(e=>e.entitiesWrite.createEntity(t))}async createEntities(t,e){if(u.isDevelopMode&&e==="remote")throw new Error("API based entity creation is not supported in the develop mode");return await d(t,R,async i=>this.runWrite(n=>n.entitiesWrite.createEntity({entity:i,source:e})).then(n=>({status:"ok",resource:n})).catch(n=>({key:i.key,status:"error",error:n})))}getEntitySources(){return this.#t}async createEntityRelations(t){await this.runWrite(e=>e.relationsWrite.createEntityRelations(t))}async updateEntity(t,e){if(u.isDevelopMode&&e.source==="remote")throw new Error("Entity update is not supported in the develop mode");return this.runWrite(i=>i.transactionsManager.transaction(()=>i.entitiesWrite.updateEntity(t,e)))}async deleteEntity(t){if(u.isDevelopMode&&t.source==="remote")throw new Error("Entity deletion is not supported in the develop mode");return this.runWrite(e=>e.entitiesWrite.deleteEntity(t))}async deleteEntities(t){await this.runWrite(async e=>{await e.entitiesWrite.deleteEntities(t)})}async createEntityRelation(t){return this.runWrite(e=>e.relationsWrite.createEntityRelation(t))}async createEntitiesRelations(t){return await d(t,R,async e=>this.runWrite(i=>i.relationsWrite.createEntityRelation(e)).then(i=>({status:"ok",resource:i})).catch(i=>({key:e.sourceKey,status:"error",error:i})))}async updateEntityRelation(t,e){const i={...e,...t,type:t.type??e.type};return this.runWrite(n=>n.relationsWrite.createEntityRelation(i))}async deleteEntityRelation(t){return this.runWrite(e=>e.relationsWrite.deleteEntityRelation(t))}async softDeleteEntitiesWithRelations({filter:t,revision:e,fileHash:i}){return this.runWrite(n=>n.entitiesWrite.softDeleteEntitiesWithRelations({filter:t,revision:e,fileHash:i}))}async updateEntityScorecardsStatus(t,e){return this.runWrite(i=>i.entitiesWrite.updateEntityScorecardsStatus(t,e))}async updateEntityScorecardsStatusIfCalculating(t,e){return this.runWrite(i=>i.entitiesWrite.updateEntityScorecardsStatusIfCalculating(t,e))}async setEntitiesAsOutdated(t){await this.runWrite(async e=>{await e.entitiesWrite.setEntitiesAsOutdated(t)})}}export{c as CatalogEntitiesService};
@@ -8,7 +8,6 @@ import { EntitiesWriteRepository } from './entities/entities-write-repository.js
8
8
  import { RelationsWriteRepository } from './relations/relations-write-repository.js';
9
9
  import { FiltersRepository } from './common/filters-repository.js';
10
10
  export declare class CatalogEntitiesRepository extends BaseRepository {
11
- #private;
12
11
  readonly entitiesRead: EntitiesReadRepository;
13
12
  readonly entitiesWrite: EntitiesWriteRepository;
14
13
  readonly relationsRead: RelationsReadRepository;
@@ -19,8 +18,6 @@ export declare class CatalogEntitiesRepository extends BaseRepository {
19
18
  get transactionsManager(): import("../../../../providers/database/transactions-manager.js").TransactionsManager;
20
19
  constructor(dbConnection: DatabaseConnection);
21
20
  sync(): Promise<void>;
22
- static getInstance(options: RepositoryInstanceOptions): Promise<CatalogEntitiesRepository>;
23
- static recreateInstance(options: RepositoryInstanceOptions): Promise<CatalogEntitiesRepository>;
24
- static resetInstance(): Promise<void>;
21
+ static create(options: RepositoryInstanceOptions): Promise<CatalogEntitiesRepository>;
25
22
  }
26
23
  //# sourceMappingURL=catalog-entities-repository.d.ts.map
@@ -1 +1 @@
1
- import{logger as n}from"../../../../tools/notifiers/logger.js";import{telemetryTraceStep as s}from"../../../../telemetry/helpers/trace-step.js";import{BaseRepository as a}from"../../../../providers/database/base-repository.js";import{DatabaseConnectionFactory as c}from"../../../../providers/database/database-connection-factory.js";import{RelationsReadRepository as o}from"./relations/relations-read-repository.js";import{RevisionRepository as f}from"./common/revision-repository.js";import{BffEntitiesReadRepository as m}from"./bffEntities/bff-entities-read-repository.js";import{EntitiesReadRepository as l}from"./entities/entities-read-repository.js";import{EntitiesWriteRepository as p}from"./entities/entities-write-repository.js";import{RelationsWriteRepository as w}from"./relations/relations-write-repository.js";import{FiltersRepository as h}from"./common/filters-repository.js";class e extends a{static#t;entitiesRead;entitiesWrite;relationsRead;relationsWrite;bffEntitiesRead;filters;revisions;get transactionsManager(){return this.databaseClient.transactionsManager}constructor(t){super(t),this.revisions=new f(t.client),this.bffEntitiesRead=new m(t.client),this.relationsRead=new o(t.client),this.entitiesRead=new l(t.client),this.entitiesWrite=new p(t.client,this.organizationId,this.projectId),this.relationsWrite=new w(t.client,this.organizationId,this.projectId),this.filters=new h(t.client)}async sync(){return s("catalog_entities.repository.sync",async()=>{await this.databaseClient.sync()})}static async#e(t){const r=await c.create(t);if(!r)throw new Error("Failed to create db connection for catalog entities repository");return new e(r)}static async getInstance(t){return await s("catalog_entities.repository.get_instance",async r=>{if(e.#t)return e.#t;try{return e.#t=await e.#e(t),e.#t}catch(i){throw n.error("Error creating db connection for catalog entities repository",i),r?.error(i),e.#t=null,i}})}static async recreateInstance(t){const r=e.#t,i=await e.#e(t);return e.#t=i,r&&await r.close(),i}static async resetInstance(){const t=e.#t;e.#t=null,t&&await t.close()}}export{e as CatalogEntitiesRepository};
1
+ import{logger as o}from"../../../../tools/notifiers/logger.js";import{telemetryTraceStep as i}from"../../../../telemetry/helpers/trace-step.js";import{BaseRepository as a}from"../../../../providers/database/base-repository.js";import{createDatabaseRepositoryInstance as n}from"../../../../providers/database/instance-cache-resolver.js";import{RelationsReadRepository as c}from"./relations/relations-read-repository.js";import{RevisionRepository as p}from"./common/revision-repository.js";import{BffEntitiesReadRepository as l}from"./bffEntities/bff-entities-read-repository.js";import{EntitiesReadRepository as m}from"./entities/entities-read-repository.js";import{EntitiesWriteRepository as y}from"./entities/entities-write-repository.js";import{RelationsWriteRepository as R}from"./relations/relations-write-repository.js";import{FiltersRepository as f}from"./common/filters-repository.js";class r extends a{entitiesRead;entitiesWrite;relationsRead;relationsWrite;bffEntitiesRead;filters;revisions;get transactionsManager(){return this.databaseClient.transactionsManager}constructor(t){super(t),this.revisions=new p(t.client),this.bffEntitiesRead=new l(t.client),this.relationsRead=new c(t.client),this.entitiesRead=new m(t.client),this.entitiesWrite=new y(t.client,this.organizationId,this.projectId),this.relationsWrite=new R(t.client,this.organizationId,this.projectId),this.filters=new f(t.client)}async sync(){return i("catalog_entities.repository.sync",async()=>{await this.databaseClient.sync()})}static async create(t){return await i("catalog_entities.repository.get_instance",async s=>{try{return await n(t,"catalog entities",r)}catch(e){throw o.error("Error creating db connection for catalog entities repository",e),s?.error(e),e}})}}export{r as CatalogEntitiesRepository};
@@ -1 +1 @@
1
- import{and as a,count as h,eq as l,isNotNull as R,or as A,sql as u}from"drizzle-orm";import{applyPagination as g}from"../../../../../providers/database/pagination/index.js";import{getEffectivePaginationLimit as D}from"../../../../../providers/database/pagination/limit.js";import{entitiesTable as t}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{entitiesAttributesTable as m}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-attributes-table.js";import{VERSION_NOT_SPECIFIED as L}from"@redocly/theme/core/constants";import{applyFilter as B}from"../../../../../providers/database/pagination/filter.js";import{getFirstRunRow as O}from"../../../../../providers/database/utils/get-first-row.js";import{logger as v}from"../../../../../tools/notifiers/logger.js";import{createEntityReadModel as f}from"../../mappers/create-entity-read-model.js";import{createEntityFieldsForSelect as w,FIELDS_TO_SELECT_FOR_ENTITY as E}from"../utils.js";import{buildRbacFilter as b}from"../utils/build-rbac-filter.js";import{buildEntitiesExclusionFilter as F}from"../utils/build-entities-exclusion-filter.js";class G{#t;constructor(e){this.#t=e}async getEntities({paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:r}){const s=F(n,r,"entities"),o=this.#t.client.select(w("entities")).from(t).leftJoin(m,l(t.key,m.entityKey)).where(a(b(i,"entities_attributes"),s)),c=this.#t.client.select(E).from(o.as("combined_entities")),S=this.#t.client.select(E).from(o.as("combined_entities")).$dynamic(),T=g(S,{...e,limit:void 0,skip:void 0,after:void 0,before:void 0}),$=this.#t.client.$count(T),C=c.$dynamic(),y=D(e),_=g(C,{...e,limit:y+1}),[k,I]=await Promise.all([_.all(),$]),p=k,N=p.length>y;return{items:p.slice(0,y).map(d=>f(d)).filter(d=>d!==null),hasMore:N,total:I}}async getEntityById(e,i){const{rbacTeams:n,excludedTypes:r,excludedEntities:s}=i||{},o=F(r,s,"entities"),c=await this.#t.client.select(w("entities")).from(t).leftJoin(m,l(t.key,m.entityKey)).where(a(l(t.id,e),b(n,"entities_attributes"),o)).get();return c?f(c):null}async getEntityKeysAndVersionsBySourceFile(e){const i=await this.#t.client.selectDistinct({keyVersion:u`${t.key} || ':' || COALESCE(${t.version}, ${L})`.as("keyVersion")}).from(t).where(a(l(t.sourceFile,e),l(t.source,"file"),R(t.key))).all();return new Set(i.map(n=>n.keyVersion))}async getEntitiesCountByTypes(){return this.#t.client.select({type:t.type,count:h()}).from(t).where(a(l(t.isCurrent,!0),l(t.isDeleted,!1))).groupBy(t.type)}async getOutdatedEntities(e){const i=this.#t.client.select(E).from(t).$dynamic(),{whereCondition:n}=B(i,e),r=A(u`scorecards_status = 'OUTDATED'`,u`scorecards_status IS NULL`),s=n?a(n,r):r;return(await i.where(s).all()).map(c=>f(c)).filter(c=>c!==null)}async getEntitiesCount(e,i,n){let r;try{const s=await this.#t.client.select({count:h()}).from(t).where(l(t.source,e));if(r=Number(s[0]?.count??0),i?.length){const o=await this.#e(e,i);r+=i.length-o}return n!==void 0&&(r-=n),{total:r}}catch(s){throw v.error("Error getting entities count:",s),new Error(`Error getting entities count: ${s?.message}`)}}async#e(e,i){const n=u.join(i.map(o=>u`(${o.key}, ${o.version}, ${o.revision})`),u`, `),r=await this.#t.client.run(u`SELECT count(*) AS count FROM entities WHERE source = ${e} AND (key, version, revision) IN (VALUES ${n})`),s=O(r);return Number(s?.count??0)}}export{G as EntitiesReadRepository};
1
+ import{and as a,count as h,eq as l,isNotNull as R,or as A,sql as u}from"drizzle-orm";import{applyPagination as b}from"../../../../../providers/database/pagination/index.js";import{getEffectivePaginationLimit as D}from"../../../../../providers/database/pagination/limit.js";import{entitiesTable as t}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{entitiesAttributesTable as m}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-attributes-table.js";import{VERSION_NOT_SPECIFIED as L}from"@redocly/theme/core/constants";import{applyFilter as B}from"../../../../../providers/database/pagination/filter.js";import{getFirstRunRow as O}from"../../../../../providers/database/utils/get-first-row.js";import{createEntityReadModel as f}from"../../mappers/create-entity-read-model.js";import{createEntityFieldsForSelect as w,FIELDS_TO_SELECT_FOR_ENTITY as p}from"../utils.js";import{buildRbacFilter as g}from"../utils/build-rbac-filter.js";import{buildEntitiesExclusionFilter as F}from"../utils/build-entities-exclusion-filter.js";class Y{#t;constructor(e){this.#t=e}async getEntities({paginationParams:e,rbacTeams:i,excludedTypes:n,excludedEntities:o}){const s=F(n,o,"entities"),r=this.#t.client.select(w("entities")).from(t).leftJoin(m,l(t.key,m.entityKey)).where(a(g(i,"entities_attributes"),s)),c=this.#t.client.select(p).from(r.as("combined_entities")),S=this.#t.client.select(p).from(r.as("combined_entities")).$dynamic(),T=b(S,{...e,limit:void 0,skip:void 0,after:void 0,before:void 0}),C=this.#t.client.$count(T),_=c.$dynamic(),y=D(e),$=b(_,{...e,limit:y+1}),[k,I]=await Promise.all([$.all(),C]),E=k,N=E.length>y;return{items:E.slice(0,y).map(d=>f(d)).filter(d=>d!==null),hasMore:N,total:I}}async getEntityById(e,i){const{rbacTeams:n,excludedTypes:o,excludedEntities:s}=i||{},r=F(o,s,"entities"),c=await this.#t.client.select(w("entities")).from(t).leftJoin(m,l(t.key,m.entityKey)).where(a(l(t.id,e),g(n,"entities_attributes"),r)).get();return c?f(c):null}async getEntityKeysAndVersionsBySourceFile(e){const i=await this.#t.client.selectDistinct({keyVersion:u`${t.key} || ':' || COALESCE(${t.version}, ${L})`.as("keyVersion")}).from(t).where(a(l(t.sourceFile,e),l(t.source,"file"),R(t.key))).all();return new Set(i.map(n=>n.keyVersion))}async getEntitiesCountByTypes(){return this.#t.client.select({type:t.type,count:h()}).from(t).where(a(l(t.isCurrent,!0),l(t.isDeleted,!1))).groupBy(t.type)}async getOutdatedEntities(e){const i=this.#t.client.select(p).from(t).$dynamic(),{whereCondition:n}=B(i,e),o=A(u`scorecards_status = 'OUTDATED'`,u`scorecards_status IS NULL`),s=n?a(n,o):o;return(await i.where(s).all()).map(c=>f(c)).filter(c=>c!==null)}async getEntitiesCount(e,i,n){const o=await this.#t.client.select({count:h()}).from(t).where(l(t.source,e));let s=Number(o[0]?.count??0);if(i?.length){const r=await this.#e(e,i);s+=i.length-r}return n!==void 0&&(s-=n),{total:s}}async#e(e,i){const n=u.join(i.map(r=>u`(${r.key}, ${r.version}, ${r.revision})`),u`, `),o=await this.#t.client.run(u`SELECT count(*) AS count FROM entities WHERE source = ${e} AND (key, version, revision) IN (VALUES ${n})`),s=O(o);return Number(s?.count??0)}}export{Y as EntitiesReadRepository};
@@ -1 +1 @@
1
- import{and as v,eq as c,isNull as R,or as x,sql as I}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as V}from"@redocly/theme/core/constants";import{sha1 as F}from"../../../../../utils/crypto/sha1.js";import{logger as y}from"../../../../../tools/notifiers/logger.js";import{envConfig as K}from"../../../../../config/env-config.js";import{entitiesTable as i}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{convertFilterToWhereCondition as O}from"../../../../../providers/database/pagination/filter.js";import{entitiesRelationsTable as a}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-relations-table.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{RevisionRepository as z}from"../common/revision-repository.js";import{VersionRepository as H}from"../common/version-repository.js";import{EntityAttributesWriteRepository as P}from"../entityAttributes/entity-attributes-write-repository.js";import{createEntityDbRecord as W}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as C}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromFileSchema as j}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{createEntityRelationDbRecordFromDto as G}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{EntitiesReadRepository as M}from"./entities-read-repository.js";import{RelationsReadRepository as $}from"../relations/relations-read-repository.js";import{RelationsWriteRepository as b}from"../relations/relations-write-repository.js";const m=15;class he{#e;#t;#n;#o;#r;#i;#a;#u;#s;constructor(e,t,s){this.#e=e,this.#r=t,this.#i=s,this.#t=new z(e),this.#n=new H(e),this.#o=new P(e),this.#a=new M(e),this.#u=new $(e),this.#s=new b(e,t,s)}async createEntity({entity:e,source:t,fileHash:s,sourceFile:n,isRootEntity:r,isDeleted:o,rbacTeams:u,errorOnSkip:h=!1,revision:f}){const{relations:l=[],...p}=e,d=F(JSON.stringify(p)),g=e.version??V,D=f??new Date().toISOString(),L=await this.#t.shouldSkipRevisionCreation(e.key,g,d,r,o);if(Array.isArray(u)&&await this.#o.upsertEntityAttributes({entityKey:e.key,rbacTeams:u,organizationId:this.#r,projectId:this.#i}),L){if(h)throw new Error("Entity validation failed: entity already exists");return null}const{shouldSetNewCurrentRevision:w,hasCurrentRevision:_}=await this.#t.getCurrentRevisionInfo({key:e.key,version:g,revision:D}),k=W({entity:{...e,revision:D,hash:d,isCurrent:w,isDefaultVersion:w,isDeleted:o,version:g},organizationId:this.#r,projectId:this.#i,source:t,sourceFile:n??null,fileHash:s??null}),{key:A,...T}=k;if(w&&_&&(await this.#t.markAllRevisionsAsNotCurrent(A),await this.#n.markAllVersionsAsNotDefault(A)),K.isDevelopMode&&!K.REDOCLY_INTERNAL_DEV)return await this.#l(k,l);const S=await this.#e.client.insert(i).values(k).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:T}).returning();return S.length?(l&&await this.#s.createEntityRelations(l.map(N=>({...N,sourceKey:e.key,targetKey:N.key}))),C(S[0])):null}async updateEntity(e,t){try{const{shouldSetNewCurrentRevision:s,hasCurrentRevision:n}=await this.#t.getCurrentRevisionInfo({key:t.key,version:e.version??t.version??V,revision:t.revision});s&&n&&(await this.#t.markAllRevisionsAsNotCurrent(t.key),await this.#n.markAllVersionsAsNotDefault(t.key));const r=W({entity:{...t,...e,hash:F(JSON.stringify({...t,...e})),isCurrent:s,isDefaultVersion:s,createdAt:t.createdAt??void 0},organizationId:this.#r,projectId:this.#i,source:"remote",sourceFile:null,fileHash:null}),{key:o,source:u,scorecardsStatus:h,...f}=r,l=await this.#e.client.insert(i).values(r).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:{...f,scorecardsStatus:I`CASE WHEN ${i.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return l.length?C(l[0]):null}catch(s){return y.error("Error updating entity",s),null}}async setEntitiesAsOutdated(e){try{const t=O(e);await this.#e.client.update(i).set({scorecardsStatus:"OUTDATED"}).where(t)}catch(t){y.error("Error updating entities as outdated",t)}}async#l(e,t){const{key:s,source:n,version:r,isDefaultVersion:o,...u}=e,l=await this.#e.client.select({id:i.id}).from(i).where(v(c(i.key,s),r?c(i.version,r):R(i.version))).limit(1).get()!=null?await this.#e.client.update(i).set(u).where(v(c(i.key,s),r?c(i.version,r):R(i.version))).returning():await this.#e.client.insert(i).values(e).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:u}).returning(),p=t?.map(d=>{const g=j({relation:d,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#r,projectId:this.#i});return this.#e.client.insert(a).values(g).onConflictDoUpdate({target:[a.sourceKey,a.targetKey,a.sourceVersion,a.targetVersion,a.sourceRevision,a.targetRevision,a.sourceToTargetRelation],set:g}).run()})??[];return await E(p,m,async d=>d),C(l[0])}async softDeleteEntitiesWithRelations({filter:e,revision:t,fileHash:s}){const r={op:"AND",conditions:[e,{field:"is_deleted",operator:"equal",value:!1}]};for(;;){const o=await this.#a.getEntities({paginationParams:{filter:r,limit:500}});if(o.items.length===0)break;const u=await this.#c({entities:o.items,revision:t,fileHash:s});await this.#d(u,t)}}async deleteEntity(e){try{return await this.#s.deleteEntityRelationsOnEntityRemoval(e),await this.#e.client.delete(i).where(c(i.id,e.id)),await this.#t.ensureDefaultAndCurrentRevisionForKey(e.key),e.id}catch(t){return y.error("Error deleting entity",t),null}}async deleteEntities(e){try{const t=O(e);if(!t)return!1;const s=await this.#e.client.delete(i).where(t).returning({key:i.key,source:i.source,isCurrent:i.isCurrent,isDefaultVersion:i.isDefaultVersion,version:i.version});if(s.length===0)return!0;const n=s.reduce((r,o)=>((o.isCurrent||o.isDefaultVersion)&&r.add(o.key),r),new Set);if(n.size===0)return!0;await E(Array.from(n),m,async r=>this.#t.ensureDefaultAndCurrentRevisionForKey(r));for(const r of s)await this.#e.client.delete(a).where(x(v(c(a.sourceKey,r.key),...r.version?[c(a.sourceVersion,r.version)]:[R(a.sourceVersion)]),v(c(a.targetKey,r.key),...r.version?[c(a.targetVersion,r.version)]:[R(a.targetVersion)])));return!0}catch(t){return y.error("Error deleting entities",t),!1}}async updateEntityScorecardsStatus(e,t){try{return(await this.#e.client.update(i).set({scorecardsStatus:t}).where(c(i.id,e)).returning()).length>0}catch(s){return y.error("Error updating entity scorecards status",s),!1}}async updateEntityScorecardsStatusIfCalculating(e,t){try{return(await this.#e.client.update(i).set({scorecardsStatus:t}).where(I`${i.id} = ${e} AND ${i.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}catch(s){return y.error("Error updating entity scorecards status if calculating",s),!1}}async#c({entities:e,revision:t,fileHash:s}){try{return(await E(e,m,async r=>{const o={type:r.type,key:r.key,title:r.title,summary:r.summary??void 0,tags:r.tags??void 0,metadata:r.metadata??void 0,git:r.git??void 0,contact:r.contact??void 0,links:r.links??void 0,version:r.version??void 0};return this.createEntity({entity:o,sourceFile:r.sourceFile??"",fileHash:s,isDeleted:!0,errorOnSkip:!1,source:r.source,revision:t})})).filter(r=>r!==null)}catch(n){return y.error("Error soft deleting entities",n),[]}}async#d(e,t){try{if(e.length===0)return!0;const s=await E(e,m,async n=>(await this.#u.getRelationsForEntity(n.key,n.version,t)).map(o=>{if(!o)return null;const u=o.direction,h=o.sourceToTargetRelation,f=o.targetKey,l=u==="outgoing"?n.key:f,p=u==="outgoing"?f:n.key,d=u==="outgoing"?h:h.startsWith("reverse:")?h.slice(8):h;return!d||!l||!p?null:G({type:d,sourceKey:l,targetKey:p,sourceVersion:n.version,targetVersion:n.version,sourceRevision:t,targetRevision:t,isDeleted:!0},this.#r,this.#i)}).filter(o=>o!==null));return await E(s.flat(),m,async n=>this.#s.upsertEntityRelation(n)),!0}catch(s){return y.error("Error soft deleting entity relations",s),!1}}}export{he as EntitiesWriteRepository};
1
+ import{and as E,eq as c,isNull as R,or as T,sql as N}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as I}from"@redocly/theme/core/constants";import{sha1 as V}from"../../../../../utils/crypto/sha1.js";import{envConfig as F}from"../../../../../config/env-config.js";import{entitiesTable as i}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{convertFilterToWhereCondition as K}from"../../../../../providers/database/pagination/filter.js";import{entitiesRelationsTable as a}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-relations-table.js";import{promiseMapLimit as m}from"../../../../../utils/async/promise-map-limit.js";import{RevisionRepository as x}from"../common/revision-repository.js";import{VersionRepository as z}from"../common/version-repository.js";import{EntityAttributesWriteRepository as H}from"../entityAttributes/entity-attributes-write-repository.js";import{createEntityDbRecord as O}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as k}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromFileSchema as P}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{createEntityRelationDbRecordFromDto as j}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{EntitiesReadRepository as G}from"./entities-read-repository.js";import{RelationsReadRepository as M}from"../relations/relations-read-repository.js";import{RelationsWriteRepository as $}from"../relations/relations-write-repository.js";const v=15;class ce{#e;#t;#n;#o;#i;#s;#a;#u;#r;constructor(e,s,r){this.#e=e,this.#i=s,this.#s=r,this.#t=new x(e),this.#n=new z(e),this.#o=new H(e),this.#a=new G(e),this.#u=new M(e),this.#r=new $(e,s,r)}async createEntity({entity:e,source:s,fileHash:r,sourceFile:o,isRootEntity:t,isDeleted:n,rbacTeams:u,errorOnSkip:h=!1,revision:f}){const{relations:l=[],...y}=e,d=V(JSON.stringify(y)),p=e.version??I,C=f??new Date().toISOString(),W=await this.#t.shouldSkipRevisionCreation(e.key,p,d,t,n);if(Array.isArray(u)&&await this.#o.upsertEntityAttributes({entityKey:e.key,rbacTeams:u,organizationId:this.#i,projectId:this.#s}),W){if(h)throw new Error("Entity validation failed: entity already exists");return null}const{shouldSetNewCurrentRevision:g,hasCurrentRevision:L}=await this.#t.getCurrentRevisionInfo({key:e.key,version:p,revision:C}),w=O({entity:{...e,revision:C,hash:d,isCurrent:g,isDefaultVersion:g,isDeleted:n,version:p},organizationId:this.#i,projectId:this.#s,source:s,sourceFile:o??null,fileHash:r??null}),{key:D,..._}=w;if(g&&L&&(await this.#t.markAllRevisionsAsNotCurrent(D),await this.#n.markAllVersionsAsNotDefault(D)),F.isDevelopMode&&!F.REDOCLY_INTERNAL_DEV)return await this.#l(w,l);const A=await this.#e.client.insert(i).values(w).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:_}).returning();return A.length?(l&&await this.#r.createEntityRelations(l.map(S=>({...S,sourceKey:e.key,targetKey:S.key}))),k(A[0])):null}async updateEntity(e,s){const{shouldSetNewCurrentRevision:r,hasCurrentRevision:o}=await this.#t.getCurrentRevisionInfo({key:s.key,version:e.version??s.version??I,revision:s.revision});r&&o&&(await this.#t.markAllRevisionsAsNotCurrent(s.key),await this.#n.markAllVersionsAsNotDefault(s.key));const t=O({entity:{...s,...e,hash:V(JSON.stringify({...s,...e})),isCurrent:r,isDefaultVersion:r,createdAt:s.createdAt??void 0},organizationId:this.#i,projectId:this.#s,source:"remote",sourceFile:null,fileHash:null}),{key:n,source:u,scorecardsStatus:h,...f}=t,l=await this.#e.client.insert(i).values(t).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:{...f,scorecardsStatus:N`CASE WHEN ${i.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return l.length?k(l[0]):null}async setEntitiesAsOutdated(e){const s=K(e);await this.#e.client.update(i).set({scorecardsStatus:"OUTDATED"}).where(s)}async#l(e,s){const{key:r,source:o,version:t,isDefaultVersion:n,...u}=e,l=await this.#e.client.select({id:i.id}).from(i).where(E(c(i.key,r),t?c(i.version,t):R(i.version))).limit(1).get()!=null?await this.#e.client.update(i).set(u).where(E(c(i.key,r),t?c(i.version,t):R(i.version))).returning():await this.#e.client.insert(i).values(e).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:u}).returning(),y=s?.map(d=>{const p=P({relation:d,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#i,projectId:this.#s});return this.#e.client.insert(a).values(p).onConflictDoUpdate({target:[a.sourceKey,a.targetKey,a.sourceVersion,a.targetVersion,a.sourceRevision,a.targetRevision,a.sourceToTargetRelation],set:p}).run()})??[];return await m(y,v,async d=>d),k(l[0])}async softDeleteEntitiesWithRelations({filter:e,revision:s,fileHash:r}){const t={op:"AND",conditions:[e,{field:"is_deleted",operator:"equal",value:!1}]};for(;;){const n=await this.#a.getEntities({paginationParams:{filter:t,limit:500}});if(n.items.length===0)break;const u=await this.#c({entities:n.items,revision:s,fileHash:r});await this.#d(u,s)}}async deleteEntity(e){return await this.#r.deleteEntityRelationsOnEntityRemoval(e),await this.#e.client.delete(i).where(c(i.id,e.id)),await this.#t.ensureDefaultAndCurrentRevisionForKey(e.key),e.id}async deleteEntities(e){const s=K(e);if(!s)return!1;const r=await this.#e.client.delete(i).where(s).returning({key:i.key,source:i.source,isCurrent:i.isCurrent,isDefaultVersion:i.isDefaultVersion,version:i.version});if(r.length===0)return!0;const o=r.reduce((t,n)=>((n.isCurrent||n.isDefaultVersion)&&t.add(n.key),t),new Set);if(o.size===0)return!0;await m(Array.from(o),v,async t=>this.#t.ensureDefaultAndCurrentRevisionForKey(t));for(const t of r)await this.#e.client.delete(a).where(T(E(c(a.sourceKey,t.key),...t.version?[c(a.sourceVersion,t.version)]:[R(a.sourceVersion)]),E(c(a.targetKey,t.key),...t.version?[c(a.targetVersion,t.version)]:[R(a.targetVersion)])));return!0}async updateEntityScorecardsStatus(e,s){return(await this.#e.client.update(i).set({scorecardsStatus:s}).where(c(i.id,e)).returning()).length>0}async updateEntityScorecardsStatusIfCalculating(e,s){return(await this.#e.client.update(i).set({scorecardsStatus:s}).where(N`${i.id} = ${e} AND ${i.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}async#c({entities:e,revision:s,fileHash:r}){return(await m(e,v,async t=>{const n={type:t.type,key:t.key,title:t.title,summary:t.summary??void 0,tags:t.tags??void 0,metadata:t.metadata??void 0,git:t.git??void 0,contact:t.contact??void 0,links:t.links??void 0,version:t.version??void 0};return this.createEntity({entity:n,sourceFile:t.sourceFile??"",fileHash:r,isDeleted:!0,errorOnSkip:!1,source:t.source,revision:s})})).filter(t=>t!==null)}async#d(e,s){if(e.length===0)return!0;const r=await m(e,v,async o=>(await this.#u.getRelationsForEntity(o.key,o.version,s)).map(n=>{if(!n)return null;const u=n.direction,h=n.sourceToTargetRelation,f=n.targetKey,l=u==="outgoing"?o.key:f,y=u==="outgoing"?f:o.key,d=u==="outgoing"?h:h.startsWith("reverse:")?h.slice(8):h;return!d||!l||!y?null:j({type:d,sourceKey:l,targetKey:y,sourceVersion:o.version,targetVersion:o.version,sourceRevision:s,targetRevision:s,isDeleted:!0},this.#i,this.#s)}).filter(n=>n!==null));return await m(r.flat(),v,async o=>this.#r.upsertEntityRelation(o)),!0}}export{ce as EntitiesWriteRepository};
@@ -1 +1 @@
1
- import{envConfig as r}from"../../../config/env-config.js";import{PRODUCT_NAME as s}from"../../../../config/product-gates.js";import{CatalogEntitiesService as c}from"../../catalog-entities/database/catalog-entities-service.js";async function d(t,a){const o=s.toLowerCase().includes("realm")||s.toLowerCase().includes("reef"),e=r.NEW_CATALOG_ENABLED??!1;if(!o||!e)return{total:0};const i=await c.getInstance({baseDbDir:t,databaseType:"local"}),{total:n}=await i.getEntitiesCount("file"),l=g(a);return{total:n+l}}function g(t){return t?.show?Object.entries(t.catalogs??{}).filter(([o,e])=>!e?.hide).length:0}export{d as getBilledCatalogBuildPagesCount};
1
+ import{isRealmOrReef as n}from"../../../utils/is-realm-or-reef.js";import{isCatalogEntitiesEnabled as l}from"../../../utils/is-catalog-entities-enabled.js";import{CatalogEntitiesService as r}from"../../catalog-entities/database/catalog-entities-service.js";async function d(t,a){if(!n()||!l())return{total:0};const e=await r.getInstance({baseDbDir:t,databaseType:"local"}),{total:i}=await e.getEntitiesCount("file"),s=g(a);return{total:i+s}}function g(t){return t?.show?Object.entries(t.catalogs??{}).filter(([o,e])=>!e?.hide).length:0}export{d as getBilledCatalogBuildPagesCount};
@@ -1,8 +1,12 @@
1
1
  import type { DatabaseClient } from '../../../../providers/database/client.js';
2
2
  import type { DatabaseScorecardsConfig, DatabaseScorecardsConfigDto } from '../../../../providers/database/databases/sqlite-db/schemas/scorecards-config-table.js';
3
+ import type { RepositoryInstanceOptions } from '../../../../providers/database/types.js';
3
4
  export declare class ScorecardsConfigRepository {
4
5
  #private;
5
6
  constructor(db: DatabaseClient);
7
+ static create(options: RepositoryInstanceOptions): Promise<ScorecardsConfigRepository>;
8
+ sync(): Promise<void>;
9
+ close(): Promise<void>;
6
10
  findActiveConfigByKey(key: string): Promise<DatabaseScorecardsConfig | null>;
7
11
  findAllActiveConfigs(): Promise<DatabaseScorecardsConfig[]>;
8
12
  insertConfig(config: DatabaseScorecardsConfigDto): Promise<DatabaseScorecardsConfig>;
@@ -1 +1 @@
1
- import{and as o,eq as n,isNull as s}from"drizzle-orm";import{scorecardsConfigTable as r}from"../../../../providers/database/databases/sqlite-db/schemas/scorecards-config-table.js";class d{#e;constructor(e){this.#e=e}async findActiveConfigByKey(e){return(await this.#e.client.select().from(r).where(o(n(r.key,e),s(r.archivedAt))).limit(1))[0]??null}async findAllActiveConfigs(){return await this.#e.client.select().from(r).where(s(r.archivedAt))}async insertConfig(e){const t=await this.#e.client.insert(r).values(e).returning();if(!t[0])throw new Error(`Failed to insert scorecard config with key: ${e.key}`);return t[0]}async updateConfigById(e,t){const i=await this.#e.client.update(r).set(t).where(n(r.id,e)).returning();if(!i[0])throw new Error(`Failed to update scorecard config with id: ${e}`);return i[0]}async archiveConfigById(e,t){const i=await this.#e.client.update(r).set({archivedAt:t}).where(n(r.id,e)).returning();if(!i[0])throw new Error(`Failed to archive scorecard config with id: ${e}`);return i[0]}}export{d as ScorecardsConfigRepository};
1
+ import{and as a,eq as n,isNull as c}from"drizzle-orm";import{scorecardsConfigTable as r}from"../../../../providers/database/databases/sqlite-db/schemas/scorecards-config-table.js";import{DatabaseConnectionFactory as s}from"../../../../providers/database/database-connection-factory.js";class o{#t;constructor(t){this.#t=t}static async create(t){const e=await s.create(t);if(!e)throw new Error("Failed to create db connection for scorecards config repository");return new o(e.client)}async sync(){await this.#t.sync()}async close(){await this.#t.close()}async findActiveConfigByKey(t){return(await this.#t.client.select().from(r).where(a(n(r.key,t),c(r.archivedAt))).limit(1))[0]??null}async findAllActiveConfigs(){return await this.#t.client.select().from(r).where(c(r.archivedAt))}async insertConfig(t){const e=await this.#t.client.insert(r).values(t).returning();if(!e[0])throw new Error(`Failed to insert scorecard config with key: ${t.key}`);return e[0]}async updateConfigById(t,e){const i=await this.#t.client.update(r).set(e).where(n(r.id,t)).returning();if(!i[0])throw new Error(`Failed to update scorecard config with id: ${t}`);return i[0]}async archiveConfigById(t,e){const i=await this.#t.client.update(r).set({archivedAt:e}).where(n(r.id,t)).returning();if(!i[0])throw new Error(`Failed to archive scorecard config with id: ${t}`);return i[0]}}export{o as ScorecardsConfigRepository};
@@ -1,10 +1,10 @@
1
1
  import type { ScorecardsConfig } from '@redocly/config';
2
2
  import type { ServiceInstanceOptions } from '../../../providers/database/types.js';
3
- import { DatabaseConnectionFactory } from '../../../providers/database/database-connection-factory.js';
4
- import { BaseRepository } from '../../../providers/database/base-repository.js';
5
- export declare class ScorecardsConfigService extends BaseRepository {
3
+ import { BaseService } from '../../../providers/database/base-service.js';
4
+ import { ScorecardsConfigRepository } from './repositories/scorecards-config-repository.js';
5
+ export declare class ScorecardsConfigService extends BaseService<ScorecardsConfigRepository> {
6
6
  #private;
7
- constructor(dbConnection: Awaited<ReturnType<typeof DatabaseConnectionFactory.create>>);
7
+ constructor(repository: ScorecardsConfigRepository, options?: ServiceInstanceOptions);
8
8
  static getInstance(options: ServiceInstanceOptions): Promise<ScorecardsConfigService>;
9
9
  syncConfig(scorecardsConfig: ScorecardsConfig, onConfigChange?: (key: string) => Promise<void>): Promise<void>;
10
10
  }
@@ -1 +1 @@
1
- import{ulid as g}from"ulid";import{logger as r}from"../../../tools/notifiers/logger.js";import{DatabaseConnectionFactory as d}from"../../../providers/database/database-connection-factory.js";import{BaseRepository as h}from"../../../providers/database/base-repository.js";import{shaHex as l}from"../../../utils/crypto/sha-hex.js";import{hasOptionsChanged as m}from"../../../plugins/catalog-entities/utils/has-options-changed.js";import{slug as y}from"../../../../utils/slugger.js";import{withTimestamp as f}from"../../../utils/time/with-timestamp.js";import{ScorecardsConfigRepository as p}from"./repositories/scorecards-config-repository.js";class o extends h{static#i;static#e;#t;constructor(t){if(!t)throw new Error("Database connection is required for ScorecardsConfigService");super(t),this.#t=new p(this.databaseClient)}static async getInstance(t){const i=m(o.#e,t);if(!o.#i||i){const n=await d.create(t);if(!n)throw new Error("Failed to create db connection for scorecards config service");o.#i=new o(n),o.#e=t}return o.#i}async syncConfig(t,i){const n=new Set;(!Array.isArray(t)||t.length===0)&&r.verbose("No scorecard configs to sync"),r.verbose(`Starting scorecard config sync for ${t.length} config(s)`);for(const s of t)try{const e=s.key;if(!/^[a-z0-9-]+$/i.test(e)){r.error(`Skipping invalid scorecard config item: key "${e}" does not match kebab-case format (letters, numbers, and dashes only)`,{scorecard:s});continue}n.add(e);const a=this.#s(s),c=await this.#t.findActiveConfigByKey(e);if(c){if(c.configHash===a){r.verbose(`Config "${e}" unchanged, skipping update`);continue}r.verbose(`Updating config "${e}"`),await this.#r(c.id,s,a)}else r.verbose(`Inserting new config "${e}"`),await this.#n(e,s,a);i&&await i(e)}catch(e){r.error("Error processing scorecard config item:",{error:e.message??e,scorecard:s})}await this.#o(n),r.verbose("Sync scorecard configuration: success")}#s(t){const i=JSON.stringify({entities:t.entities,levels:t.levels});return l(i)}async#n(t,i,n){await this.#t.insertConfig(f({id:`sc_${g()}`,key:t,slug:y(t),name:i.name,description:i.description??null,entitiesFilter:JSON.stringify(i.entities),levels:JSON.stringify(i.levels),configHash:n,archivedAt:null}))}async#r(t,i,n){const s=f({entitiesFilter:JSON.stringify(i.entities),levels:JSON.stringify(i.levels),configHash:n,archivedAt:null},{fields:["updatedAt"]});await this.#t.updateConfigById(t,s)}async#o(t){const i=await this.#t.findAllActiveConfigs(),n=new Date().toISOString();for(const s of i)if(!t.has(s.key)){r.verbose(`Archiving removed config "${s.key}"`);try{await this.#t.archiveConfigById(s.id,n)}catch(e){r.error(`Error archiving config item "${s.key}":`,e.message??e)}}}}export{o as ScorecardsConfigService};
1
+ import{ulid as l}from"ulid";import{logger as s}from"../../../tools/notifiers/logger.js";import{BaseService as h}from"../../../providers/database/base-service.js";import{InstanceCacheResolver as y}from"../../../providers/database/instance-cache-resolver.js";import{shaHex as m}from"../../../utils/crypto/sha-hex.js";import{slug as v}from"../../../../utils/slugger.js";import{withTimestamp as f}from"../../../utils/time/with-timestamp.js";import{ScorecardsConfigRepository as g}from"./repositories/scorecards-config-repository.js";class c extends h{static#e=new y(async e=>new c(await g.create(e),e));constructor(e,r){super(r,{repository:e,createRepository:g.create})}static async getInstance(e){return c.#e.get(e)}async syncConfig(e,r){const n=new Set;(!Array.isArray(e)||e.length===0)&&s.verbose("No scorecard configs to sync"),s.verbose(`Starting scorecard config sync for ${e.length} config(s)`);for(const t of e)try{const i=t.key;if(!/^[a-z0-9-]+$/i.test(i)){s.error(`Skipping invalid scorecard config item: key "${i}" does not match kebab-case format (letters, numbers, and dashes only)`,{scorecard:t});continue}n.add(i);const o=this.#i(t),a=await this.runRead(d=>d.findActiveConfigByKey(i));if(a){if(a.configHash===o){s.verbose(`Config "${i}" unchanged, skipping update`);continue}s.verbose(`Updating config "${i}"`),await this.#r(a.id,t,o)}else s.verbose(`Inserting new config "${i}"`),await this.#t(i,t,o);r&&await r(i)}catch(i){s.error("Error processing scorecard config item:",{error:i.message??i,scorecard:t})}await this.#s(n),s.verbose("Sync scorecard configuration: success")}#i(e){const r=JSON.stringify({entities:e.entities,levels:e.levels});return m(r)}async#t(e,r,n){await this.runWrite(t=>t.insertConfig(f({id:`sc_${l()}`,key:e,slug:v(e),name:r.name,description:r.description??null,entitiesFilter:JSON.stringify(r.entities),levels:JSON.stringify(r.levels),configHash:n,archivedAt:null})))}async#r(e,r,n){const t=f({entitiesFilter:JSON.stringify(r.entities),levels:JSON.stringify(r.levels),configHash:n,archivedAt:null},{fields:["updatedAt"]});await this.runWrite(i=>i.updateConfigById(e,t))}async#s(e){const r=await this.runRead(t=>t.findAllActiveConfigs()),n=new Date().toISOString();for(const t of r)if(!e.has(t.key)){s.verbose(`Archiving removed config "${t.key}"`);try{await this.runWrite(i=>i.archiveConfigById(t.id,n))}catch(i){s.error(`Error archiving config item "${t.key}":`,i.message??i)}}}}export{c as ScorecardsConfigService};
@@ -0,0 +1,25 @@
1
+ import type { ServiceInstanceOptions } from './types.js';
2
+ type SyncableRepository = {
3
+ sync: () => Promise<void>;
4
+ };
5
+ type RunOptions<T> = {
6
+ onRepositoryMissing?: () => Promise<T> | T;
7
+ skipSync?: boolean;
8
+ };
9
+ type BaseServiceConfig<R extends SyncableRepository | null> = {
10
+ repository: NonNullable<R>;
11
+ createRepository?: (options: ServiceInstanceOptions) => Promise<NonNullable<R>>;
12
+ };
13
+ export declare abstract class BaseService<R extends SyncableRepository | null> {
14
+ #private;
15
+ protected constructor(options: ServiceInstanceOptions | undefined, config: BaseServiceConfig<R>);
16
+ protected get isLocalDbMode(): boolean;
17
+ protected getRepository(): R;
18
+ protected setRepository(repository: NonNullable<R>): void;
19
+ protected recreateRepository(): Promise<NonNullable<R> | null>;
20
+ protected syncRepositoryIfNeeded(): Promise<void>;
21
+ protected runRead<T>(run: (repo: NonNullable<R>) => Promise<T>, options?: RunOptions<T>): Promise<T>;
22
+ protected runWrite<T>(run: (repo: NonNullable<R>) => Promise<T>, options?: RunOptions<T>): Promise<T>;
23
+ }
24
+ export {};
25
+ //# sourceMappingURL=base-service.d.ts.map
@@ -0,0 +1 @@
1
+ import{envConfig as a}from"../../config/env-config.js";import{logger as n}from"../../tools/notifiers/logger.js";import{SQLD_REMOTE_DATABASE_URL_NO_DEPLOYMENT_YET_VAR as p}from"../../constants/plugins/catalog-entities.js";import{HTTP_TRANSIENT_RETRY as o,describeTransientError as c,isTransientSqldError as R,withTransientErrorRetry as s}from"./transient-sqld-error.js";class w{#i;#r;#o=null;#s;constructor(t,r){this.#i=t,this.#r=l(t)==="local",this.#o=r.repository,this.#s=r.createRepository}get isLocalDbMode(){return this.#r}getRepository(){return this.#o}setRepository(t){this.#o=t}async recreateRepository(){return!this.#s||!this.#i?null:this.#s(this.#i)}async syncRepositoryIfNeeded(){if(this.#r)return;const t=this.getRepository();t&&await s(()=>t.sync(),o)}async runRead(t,r){if(r?.skipSync||await this.syncRepositoryIfNeeded(),this.#r)try{return await this.#t(t,r)}catch(e){throw this.#e("read",e),e}try{return await s(()=>this.#t(t,r),o)}catch(e){throw this.#e("read",e),e}}async runWrite(t,r){if(this.#r)try{return await this.#t(t,r)}catch(e){throw this.#e("write",e),e}try{return await s(async()=>{try{return await this.#t(t,r)}catch(e){throw await this.#a(e),e}},o)}catch(e){throw this.#e("write",e),e}}async#t(t,r){const e=this.getRepository();if(!e){if(r?.onRepositoryMissing)return await r.onRepositoryMissing();throw new Error("Database repository is not initialized")}return t(e)}async#a(t){if(R(t))try{const r=await this.recreateRepository();r&&this.setRepository(r)}catch(r){n.warn(`Reconnect attempt failed, letting original error propagate: ${c(r)}`)}}#e(t,r){const e=this.constructor.name,h=r instanceof Error?c(r):String(r),y=this.#r?"local-no-retry":"remote-retry-enabled";n.error(`[${e}] ${t} operation failed (${y}): ${h}`,r)}}function l(i){if(i?.databaseType)return i.databaseType;const t=a.SQLD_REMOTE_DATABASE_URL||i?.sqldRemoteDatabaseUrl,r=a.SQLD_REMOTE_DATABASE_AUTH_TOKEN||i?.sqldRemoteDatabaseAuthToken;return t&&t!==p&&r?"remote":"local"}export{w as BaseService};
@@ -1 +1 @@
1
- import{logger as o}from"../../tools/notifiers/logger.js";import{envConfig as a}from"../../config/env-config.js";import{DatabaseConnectionFactory as i}from"./database-connection-factory.js";class t{static#t=!1;static async init(e){if(!(a.REDOCLY_INTERNAL_DEV||a.CI)&&!t.#t)try{await t.#e(e)==="PRECONNECTED"&&(t.#t=!0)}catch(r){o.error("Failed to preconnect to sqld remote database",r)}}static#e=async e=>await i.create({baseDbDir:e,databaseType:"remote"})?(o.info("Sqld remote database preconnected"),"PRECONNECTED"):(o.warn("Sqld remote database preconnect failed"),"NOT_PRECONNECTED")}export{t as DatabasePreconnectService};
1
+ import{logger as o}from"../../tools/notifiers/logger.js";import{envConfig as r}from"../../config/env-config.js";import{DatabaseConnectionFactory as i}from"./database-connection-factory.js";class e{static#e=!1;static async init(t){if(!(r.REDOCLY_INTERNAL_DEV||r.CI||r.isDevelopMode)&&!e.#e)try{await e.#t(t)==="PRECONNECTED"&&(e.#e=!0)}catch(a){o.error("Failed to preconnect to sqld remote database",a)}}static#t=async t=>await i.create({baseDbDir:t,databaseType:"remote"})?(o.info("Sqld remote database preconnected"),"PRECONNECTED"):(o.warn("Sqld remote database preconnect failed"),"NOT_PRECONNECTED")}export{e as DatabasePreconnectService};
@@ -0,0 +1,19 @@
1
+ import type { DatabaseConnection, RepositoryInstanceOptions } from './types.js';
2
+ type WithRemoveExisting = {
3
+ removeExisting?: boolean;
4
+ };
5
+ /**
6
+ * Caches a single async-created instance (a database repository, or a service that
7
+ * wraps one) and reuses it across calls.
8
+ *
9
+ * A new instance is created only when none exists yet, or when the creation options
10
+ * change.
11
+ */
12
+ export declare class InstanceCacheResolver<TInstance, TOptions extends WithRemoveExisting> {
13
+ #private;
14
+ constructor(create: (options: TOptions) => Promise<TInstance>);
15
+ get(options: TOptions): Promise<TInstance>;
16
+ }
17
+ export declare function createDatabaseRepositoryInstance<TRepository>(options: RepositoryInstanceOptions, repositoryName: string, repositoryClass: new (dbConnection: DatabaseConnection) => TRepository): Promise<TRepository>;
18
+ export {};
19
+ //# sourceMappingURL=instance-cache-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ import{deepStrictEqual as o}from"node:assert";import{DatabaseConnectionFactory as i}from"./database-connection-factory.js";class h{#t;#e;#n;constructor(e){this.#n=e}async get(e){const n=a(e);return this.#t&&!s(this.#e,n)?this.#t:(this.#t=await this.#n(n),this.#e=n,this.#t)}}async function u(t,e,n){const r=await i.create(t);if(!r)throw new Error(`Failed to create db connection for ${e} repository`);return new n(r)}function a(t){return{...t,removeExisting:t.removeExisting??!1}}function s(t,e){if(!t)return!1;try{return o(t,e),!1}catch{return!0}}export{h as InstanceCacheResolver,u as createDatabaseRepositoryInstance};
@@ -0,0 +1,2 @@
1
+ export declare function isRealmOrReef(): boolean;
2
+ //# sourceMappingURL=is-realm-or-reef.d.ts.map
@@ -0,0 +1 @@
1
+ import{PRODUCT_NAME as e}from"../../config/product-gates.js";function o(){return e.toLowerCase().includes("realm")||e.toLowerCase().includes("reef")}export{o as isRealmOrReef};
@@ -1 +1 @@
1
- import{ServerRoutes as c}from"../../constants/common.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{telemetry as s}from"../telemetry/index.js";import{installDevRoutes as p,installProdRoutes as f}from"./routes/index.js";import{createRouter as v}from"./router.js";import{readStaticAsset as l}from"./node-asset-reader.js";import{startHttpServer as u}from"./http.js";import{listenStore as S}from"./store-ws.js";import{ejectComponentDataHandler as d}from"./routes/eject.js";import{attachWsServer as D}from"./ws.js";import{DatabasePreconnectService as w}from"../providers/database/database-preconnect-service.js";import{KvService as E}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as R}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isCatalogEntitiesEnabled as b}from"../utils/is-catalog-entities-enabled.js";import{isScorecardsEnabled as g}from"../utils/is-scorecards-enabled.js";async function J(r,o,i){s.initialize(!0);const{port:a=4e3}=i,e=v();if(e.get(c.EJECT_COMPONENT,d(r)),p(e,r),f(e,r,{readStaticAsset:l,resolveRouteData:t=>r.resolveRouteStaticData(t,o)}),b()){await w.init(r.serverOutDir);const t=await E.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>t.clearExpired(),300*1e3)}try{await r.userCodeReady;const t=await u(e,a),m=D(t);S(r,m),g(r.config)&&await R(r.serverOutDir,r.config.scorecards)}catch(t){await n.panic(t)}}export{J as startDevServer};
1
+ import{ServerRoutes as c}from"../../constants/common.js";import{reporter as n}from"../tools/notifiers/reporter.js";import{logger as s}from"../tools/notifiers/logger.js";import{telemetry as p}from"../telemetry/index.js";import{installDevRoutes as f,installProdRoutes as v}from"./routes/index.js";import{createRouter as l}from"./router.js";import{readStaticAsset as u}from"./node-asset-reader.js";import{startHttpServer as d}from"./http.js";import{listenStore as S}from"./store-ws.js";import{ejectComponentDataHandler as D}from"./routes/eject.js";import{attachWsServer as R}from"./ws.js";import{DatabasePreconnectService as w}from"../providers/database/database-preconnect-service.js";import{KvService as g}from"../persistence/kv/services/kv-service.js";import{runScorecardsWorker as O}from"../plugins/scorecards/workers/run-scorecards-worker.js";import{isScorecardsEnabled as E}from"../utils/is-scorecards-enabled.js";import{isRealmOrReef as b}from"../utils/is-realm-or-reef.js";async function J(r,i,a){p.initialize(!0);const{port:m=4e3}=a,t=l();if(t.get(c.EJECT_COMPONENT,D(r)),f(t,r),v(t,r,{readStaticAsset:u,resolveRouteData:e=>r.resolveRouteStaticData(e,i)}),b()){await w.init(r.serverOutDir);const e=await g.getInstance({baseDbDir:r.serverOutDir});setInterval(()=>{e.clearExpired().catch(o=>{s.error("Failed to clear expired KV entries",o)})},300*1e3)}try{await r.userCodeReady;const e=await d(t,m),o=R(e);S(r,o),E(r.config)&&await O(r.serverOutDir,r.config.scorecards)}catch(e){await n.panic(e)}}export{J as startDevServer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/realm",
3
- "version": "0.134.0-next.2",
3
+ "version": "0.134.0-next.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,2 +0,0 @@
1
- export declare function hasOptionsChanged<T>(options1: T | undefined, options2: T): boolean;
2
- //# sourceMappingURL=has-options-changed.d.ts.map
@@ -1 +0,0 @@
1
- import{deepStrictEqual as e}from"node:assert";function n(r,t){if(!r)return!1;try{return e(r,t),!1}catch{return!0}}export{n as hasOptionsChanged};