@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.
- package/CHANGELOG.md +15 -0
- package/dist/client/app/utils/getBlocksHeight.d.ts +5 -14
- package/dist/client/app/utils/getBlocksHeight.js +1 -1
- package/dist/client/app/utils/syncScrollWithEditor.js +1 -1
- package/dist/server/node-bundle-entry.js +1 -1
- package/dist/server/persistence/cache/repositories/cache-repository.js +1 -1
- package/dist/server/persistence/file-hashes/repositories/file-hashes-repository.js +1 -1
- package/dist/server/persistence/kv/repositories/kv-repository.d.ts +1 -1
- package/dist/server/persistence/kv/repositories/kv-repository.js +2 -2
- package/dist/server/persistence/kv/services/kv-service.d.ts +3 -2
- package/dist/server/persistence/kv/services/kv-service.js +1 -1
- package/dist/server/plugins/catalog-entities/database/catalog-entities-service.d.ts +2 -1
- package/dist/server/plugins/catalog-entities/database/catalog-entities-service.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.d.ts +1 -4
- package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-read-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
- package/dist/server/plugins/entitlements/utils/get-billed-catalog-build-pages-count.js +1 -1
- package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.d.ts +4 -0
- package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.js +1 -1
- package/dist/server/plugins/scorecards/database/scorecards-config-service.d.ts +4 -4
- package/dist/server/plugins/scorecards/database/scorecards-config-service.js +1 -1
- package/dist/server/providers/database/base-service.d.ts +25 -0
- package/dist/server/providers/database/base-service.js +1 -0
- package/dist/server/providers/database/database-preconnect-service.js +1 -1
- package/dist/server/providers/database/instance-cache-resolver.d.ts +19 -0
- package/dist/server/providers/database/instance-cache-resolver.js +1 -0
- package/dist/server/utils/is-realm-or-reef.d.ts +2 -0
- package/dist/server/utils/is-realm-or-reef.js +1 -0
- package/dist/server/web-server/dev-server.js +1 -1
- package/package.json +1 -1
- package/dist/server/plugins/catalog-entities/utils/has-options-changed.d.ts +0 -2
- 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
|
|
7
|
-
* A block is defined as content between two heading elements or from start/end of
|
|
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
|
|
13
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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{
|
|
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
|
|
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
|
|
2
|
-
All changes and data will only persist locally and will not be synced remotely.`),this.#
|
|
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
|
|
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
|
|
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
|
|
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};
|
package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/dist/server/plugins/catalog-entities/database/repositories/catalog-entities-repository.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as
|
|
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
|
|
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
|
|
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{
|
|
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};
|
package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.d.ts
CHANGED
|
@@ -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>;
|
package/dist/server/plugins/scorecards/database/repositories/scorecards-config-repository.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{and as
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
export declare class ScorecardsConfigService extends
|
|
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(
|
|
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
|
|
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
|
|
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 @@
|
|
|
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{
|
|
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 +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};
|