@webiny/api-core-sql 0.0.0-unstable.b6d7105cee → 0.0.0-unstable.e9333b3f0b

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.
@@ -0,0 +1,18 @@
1
+ import type { Knex } from "knex";
2
+ export interface ApiCoreSqlConfig {
3
+ knex: Knex;
4
+ tableNamePrefix?: string;
5
+ }
6
+ /**
7
+ * Registers the SQL implementation of ApiCoreStorageOperationsFactory. ApiCoreFeature.register
8
+ * resolves + builds it. Mirrors ApiCoreDdbFeature.
9
+ *
10
+ * The storage operations are built once, here, and `create()` hands out the same object. It runs on
11
+ * every request (ApiCoreFeature is per-request), and building per request created a new TableManager
12
+ * each time: every one stayed in the global test registry, so a long-running server leaked one per
13
+ * request, and each started with no verified tables. The operations hold no per-request state.
14
+ */
15
+ export declare const ApiCoreSqlFeature: {
16
+ name: string;
17
+ register(container: import("@webiny/di").Container, context: ApiCoreSqlConfig): void;
18
+ };
@@ -0,0 +1,18 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { ApiCoreStorageOperationsFactory } from "@webiny/api-core";
3
+ import { createApiCoreSql } from "./createApiCoreSql.js";
4
+ const ApiCoreSqlFeature = createFeature({
5
+ name: "ApiCoreSql",
6
+ register (container, { knex, tableNamePrefix }) {
7
+ const storageOperations = createApiCoreSql({
8
+ knex,
9
+ tableNamePrefix
10
+ });
11
+ container.registerInstance(ApiCoreStorageOperationsFactory, {
12
+ create: ()=>storageOperations
13
+ });
14
+ }
15
+ });
16
+ export { ApiCoreSqlFeature };
17
+
18
+ //# sourceMappingURL=ApiCoreSqlFeature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiCoreSqlFeature.js","sources":["../src/ApiCoreSqlFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport type { Knex } from \"knex\";\nimport { ApiCoreStorageOperationsFactory } from \"@webiny/api-core\";\nimport { createApiCoreSql } from \"./createApiCoreSql.js\";\n\nexport interface ApiCoreSqlConfig {\n knex: Knex;\n tableNamePrefix?: string;\n}\n\n/**\n * Registers the SQL implementation of ApiCoreStorageOperationsFactory. ApiCoreFeature.register\n * resolves + builds it. Mirrors ApiCoreDdbFeature.\n *\n * The storage operations are built once, here, and `create()` hands out the same object. It runs on\n * every request (ApiCoreFeature is per-request), and building per request created a new TableManager\n * each time: every one stayed in the global test registry, so a long-running server leaked one per\n * request, and each started with no verified tables. The operations hold no per-request state.\n */\nexport const ApiCoreSqlFeature = createFeature<ApiCoreSqlConfig>({\n name: \"ApiCoreSql\",\n register(container, { knex, tableNamePrefix }) {\n const storageOperations = createApiCoreSql({ knex, tableNamePrefix });\n\n container.registerInstance(ApiCoreStorageOperationsFactory, {\n create: () => storageOperations\n });\n }\n});\n"],"names":["ApiCoreSqlFeature","createFeature","container","knex","tableNamePrefix","storageOperations","createApiCoreSql","ApiCoreStorageOperationsFactory"],"mappings":";;;AAmBO,MAAMA,oBAAoBC,cAAgC;IAC7D,MAAM;IACN,UAASC,SAAS,EAAE,EAAEC,IAAI,EAAEC,eAAe,EAAE;QACzC,MAAMC,oBAAoBC,iBAAiB;YAAEH;YAAMC;QAAgB;QAEnEF,UAAU,gBAAgB,CAACK,iCAAiC;YACxD,QAAQ,IAAMF;QAClB;IACJ;AACJ"}
package/TableManager.d.ts CHANGED
@@ -8,9 +8,12 @@ export declare class TableManager implements ITableManager {
8
8
  private readonly knex;
9
9
  private readonly prefix;
10
10
  private readonly verified;
11
+ private readonly pending;
11
12
  constructor(knex: Knex, prefix?: string);
12
13
  reset(): void;
13
14
  resolve(tableName: string): string;
14
15
  ensure(tableName: string, creator: (table: Knex.CreateTableBuilder) => void): Promise<void>;
16
+ private ensureTable;
17
+ private createTable;
15
18
  }
16
19
  export {};
package/TableManager.js CHANGED
@@ -1,6 +1,7 @@
1
1
  class TableManager {
2
2
  constructor(knex, prefix = ""){
3
3
  this.verified = new Set();
4
+ this.pending = new Map();
4
5
  this.knex = knex;
5
6
  this.prefix = prefix ? `${prefix}_` : "";
6
7
  const g = globalThis;
@@ -9,6 +10,7 @@ class TableManager {
9
10
  }
10
11
  reset() {
11
12
  this.verified.clear();
13
+ this.pending.clear();
12
14
  }
13
15
  resolve(tableName) {
14
16
  return `${this.prefix}${tableName}`;
@@ -16,10 +18,28 @@ class TableManager {
16
18
  async ensure(tableName, creator) {
17
19
  const resolved = this.resolve(tableName);
18
20
  if (this.verified.has(resolved)) return;
21
+ const pending = this.pending.get(resolved);
22
+ if (pending) return pending;
23
+ const check = this.ensureTable(resolved, creator).finally(()=>{
24
+ if (this.pending.get(resolved) === check) this.pending.delete(resolved);
25
+ });
26
+ this.pending.set(resolved, check);
27
+ return check;
28
+ }
29
+ async ensureTable(resolved, creator) {
19
30
  const exists = await this.knex.schema.hasTable(resolved);
20
- if (!exists) await this.knex.schema.createTable(resolved, creator);
31
+ if (!exists) await this.createTable(resolved, creator);
21
32
  this.verified.add(resolved);
22
33
  }
34
+ async createTable(resolved, creator) {
35
+ try {
36
+ await this.knex.schema.createTable(resolved, creator);
37
+ } catch (err) {
38
+ const exists = await this.knex.schema.hasTable(resolved);
39
+ if (exists) return;
40
+ throw err;
41
+ }
42
+ }
23
43
  }
24
44
  export { TableManager };
25
45
 
@@ -1 +1 @@
1
- {"version":3,"file":"TableManager.js","sources":["../src/TableManager.ts"],"sourcesContent":["import type { Knex } from \"knex\";\n\ninterface ITableManager {\n reset(): void;\n resolve(tableName: string): string;\n ensure(tableName: string, creator: (table: Knex.CreateTableBuilder) => void): Promise<void>;\n}\n\nexport class TableManager implements ITableManager {\n private readonly knex: Knex;\n private readonly prefix: string;\n private readonly verified = new Set<string>();\n\n constructor(knex: Knex, prefix: string = \"\") {\n this.knex = knex;\n this.prefix = prefix ? `${prefix}_` : \"\";\n\n const g = globalThis as Record<string, unknown>;\n const managers = (g.__sqlTableManagers ??= []) as ITableManager[];\n managers.push(this);\n }\n\n public reset(): void {\n this.verified.clear();\n }\n\n public resolve(tableName: string): string {\n return `${this.prefix}${tableName}`;\n }\n\n public async ensure(\n tableName: string,\n creator: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n const resolved = this.resolve(tableName);\n\n if (this.verified.has(resolved)) {\n return;\n }\n\n const exists = await this.knex.schema.hasTable(resolved);\n\n if (!exists) {\n await this.knex.schema.createTable(resolved, creator);\n }\n\n this.verified.add(resolved);\n }\n}\n"],"names":["TableManager","knex","prefix","Set","g","globalThis","managers","tableName","creator","resolved","exists"],"mappings":"AAQO,MAAMA;IAKT,YAAYC,IAAU,EAAEC,SAAiB,EAAE,CAAE;aAF5B,QAAQ,GAAG,IAAIC;QAG5B,IAAI,CAAC,IAAI,GAAGF;QACZ,IAAI,CAAC,MAAM,GAAGC,SAAS,GAAGA,OAAO,CAAC,CAAC,GAAG;QAEtC,MAAME,IAAIC;QACV,MAAMC,WAAYF,EAAE,kBAAkB,KAAK,EAAE;QAC7CE,SAAS,IAAI,CAAC,IAAI;IACtB;IAEO,QAAc;QACjB,IAAI,CAAC,QAAQ,CAAC,KAAK;IACvB;IAEO,QAAQC,SAAiB,EAAU;QACtC,OAAO,GAAG,IAAI,CAAC,MAAM,GAAGA,WAAW;IACvC;IAEA,MAAa,OACTA,SAAiB,EACjBC,OAAiD,EACpC;QACb,MAAMC,WAAW,IAAI,CAAC,OAAO,CAACF;QAE9B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACE,WAClB;QAGJ,MAAMC,SAAS,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACD;QAE/C,IAAI,CAACC,QACD,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAACD,UAAUD;QAGjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACC;IACtB;AACJ"}
1
+ {"version":3,"file":"TableManager.js","sources":["../src/TableManager.ts"],"sourcesContent":["import type { Knex } from \"knex\";\n\ninterface ITableManager {\n reset(): void;\n resolve(tableName: string): string;\n ensure(tableName: string, creator: (table: Knex.CreateTableBuilder) => void): Promise<void>;\n}\n\nexport class TableManager implements ITableManager {\n private readonly knex: Knex;\n private readonly prefix: string;\n private readonly verified = new Set<string>();\n private readonly pending = new Map<string, Promise<void>>();\n\n constructor(knex: Knex, prefix: string = \"\") {\n this.knex = knex;\n this.prefix = prefix ? `${prefix}_` : \"\";\n\n const g = globalThis as Record<string, unknown>;\n const managers = (g.__sqlTableManagers ??= []) as ITableManager[];\n managers.push(this);\n }\n\n public reset(): void {\n this.verified.clear();\n this.pending.clear();\n }\n\n public resolve(tableName: string): string {\n return `${this.prefix}${tableName}`;\n }\n\n public async ensure(\n tableName: string,\n creator: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n const resolved = this.resolve(tableName);\n\n if (this.verified.has(resolved)) {\n return;\n }\n\n // One TableManager serves every request, so concurrent first requests share a single\n // hasTable+createTable instead of racing into \"table already exists\". Cleared on failure so\n // a later call can retry.\n const pending = this.pending.get(resolved);\n if (pending) {\n return pending;\n }\n\n const check = this.ensureTable(resolved, creator).finally(() => {\n // Only remove our own entry: after reset() a newer check may own this table's slot.\n if (this.pending.get(resolved) === check) {\n this.pending.delete(resolved);\n }\n });\n this.pending.set(resolved, check);\n\n return check;\n }\n\n private async ensureTable(\n resolved: string,\n creator: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n const exists = await this.knex.schema.hasTable(resolved);\n\n if (!exists) {\n await this.createTable(resolved, creator);\n }\n\n this.verified.add(resolved);\n }\n\n private async createTable(\n resolved: string,\n creator: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n try {\n await this.knex.schema.createTable(resolved, creator);\n } catch (err) {\n // Another process (or connection) may have created it in the meantime. If it exists now,\n // that's success; otherwise the failure is real.\n const exists = await this.knex.schema.hasTable(resolved);\n if (exists) {\n return;\n }\n throw err;\n }\n }\n}\n"],"names":["TableManager","knex","prefix","Set","Map","g","globalThis","managers","tableName","creator","resolved","pending","check","exists","err"],"mappings":"AAQO,MAAMA;IAMT,YAAYC,IAAU,EAAEC,SAAiB,EAAE,CAAE;aAH5B,QAAQ,GAAG,IAAIC;aACf,OAAO,GAAG,IAAIC;QAG3B,IAAI,CAAC,IAAI,GAAGH;QACZ,IAAI,CAAC,MAAM,GAAGC,SAAS,GAAGA,OAAO,CAAC,CAAC,GAAG;QAEtC,MAAMG,IAAIC;QACV,MAAMC,WAAYF,EAAE,kBAAkB,KAAK,EAAE;QAC7CE,SAAS,IAAI,CAAC,IAAI;IACtB;IAEO,QAAc;QACjB,IAAI,CAAC,QAAQ,CAAC,KAAK;QACnB,IAAI,CAAC,OAAO,CAAC,KAAK;IACtB;IAEO,QAAQC,SAAiB,EAAU;QACtC,OAAO,GAAG,IAAI,CAAC,MAAM,GAAGA,WAAW;IACvC;IAEA,MAAa,OACTA,SAAiB,EACjBC,OAAiD,EACpC;QACb,MAAMC,WAAW,IAAI,CAAC,OAAO,CAACF;QAE9B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACE,WAClB;QAMJ,MAAMC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAACD;QACjC,IAAIC,SACA,OAAOA;QAGX,MAAMC,QAAQ,IAAI,CAAC,WAAW,CAACF,UAAUD,SAAS,OAAO,CAAC;YAEtD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAACC,cAAcE,OAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAACF;QAE5B;QACA,IAAI,CAAC,OAAO,CAAC,GAAG,CAACA,UAAUE;QAE3B,OAAOA;IACX;IAEA,MAAc,YACVF,QAAgB,EAChBD,OAAiD,EACpC;QACb,MAAMI,SAAS,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACH;QAE/C,IAAI,CAACG,QACD,MAAM,IAAI,CAAC,WAAW,CAACH,UAAUD;QAGrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACC;IACtB;IAEA,MAAc,YACVA,QAAgB,EAChBD,OAAiD,EACpC;QACb,IAAI;YACA,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAACC,UAAUD;QACjD,EAAE,OAAOK,KAAK;YAGV,MAAMD,SAAS,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAACH;YAC/C,IAAIG,QACA;YAEJ,MAAMC;QACV;IACJ;AACJ"}
package/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { Knex } from "knex";
2
2
  export { createApiCoreSql } from "./createApiCoreSql.js";
3
+ export { ApiCoreSqlFeature } from "./ApiCoreSqlFeature.js";
4
+ export type { ApiCoreSqlConfig } from "./ApiCoreSqlFeature.js";
3
5
  export { getSqlTablePrefix } from "./getSqlTablePrefix.js";
4
6
  export { SqlServiceManifestLoader } from "./serviceDiscovery/index.js";
5
7
  export { KnexClient } from "./feature/KnexClient/index.js";
package/index.js CHANGED
@@ -4,6 +4,7 @@ const registerSQLCore = ({ knex })=>createRegisterExtensionPlugin(async (context
4
4
  KnexClientFeature.register(context.container, knex);
5
5
  });
6
6
  export { createApiCoreSql } from "./createApiCoreSql.js";
7
+ export { ApiCoreSqlFeature } from "./ApiCoreSqlFeature.js";
7
8
  export { getSqlTablePrefix } from "./getSqlTablePrefix.js";
8
9
  export { SqlServiceManifestLoader } from "./serviceDiscovery/index.js";
9
10
  export { KnexClient, registerSQLCore };
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { createRegisterExtensionPlugin } from \"@webiny/handler\";\nimport type { Knex } from \"knex\";\nimport { KnexClientFeature } from \"./feature/KnexClient/index.js\";\n\nexport { createApiCoreSql } from \"./createApiCoreSql.js\";\nexport { getSqlTablePrefix } from \"./getSqlTablePrefix.js\";\nexport { SqlServiceManifestLoader } from \"./serviceDiscovery/index.js\";\n\nexport { KnexClient } from \"./feature/KnexClient/index.js\";\n\ninterface IRegisterApiCoreSqlExtension {\n knex: Knex;\n}\n\nexport const registerSQLCore = ({ knex }: IRegisterApiCoreSqlExtension) => {\n return createRegisterExtensionPlugin(async context => {\n KnexClientFeature.register(context.container, knex);\n });\n};\n"],"names":["registerSQLCore","knex","createRegisterExtensionPlugin","context","KnexClientFeature"],"mappings":";;AAcO,MAAMA,kBAAkB,CAAC,EAAEC,IAAI,EAAgC,GAC3DC,8BAA8B,OAAMC;QACvCC,kBAAkB,QAAQ,CAACD,QAAQ,SAAS,EAAEF;IAClD"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { createRegisterExtensionPlugin } from \"@webiny/handler\";\nimport type { Knex } from \"knex\";\nimport { KnexClientFeature } from \"./feature/KnexClient/index.js\";\n\nexport { createApiCoreSql } from \"./createApiCoreSql.js\";\nexport { ApiCoreSqlFeature } from \"./ApiCoreSqlFeature.js\";\nexport type { ApiCoreSqlConfig } from \"./ApiCoreSqlFeature.js\";\nexport { getSqlTablePrefix } from \"./getSqlTablePrefix.js\";\nexport { SqlServiceManifestLoader } from \"./serviceDiscovery/index.js\";\n\nexport { KnexClient } from \"./feature/KnexClient/index.js\";\n\ninterface IRegisterApiCoreSqlExtension {\n knex: Knex;\n}\n\nexport const registerSQLCore = ({ knex }: IRegisterApiCoreSqlExtension) => {\n return createRegisterExtensionPlugin(async context => {\n KnexClientFeature.register(context.container, knex);\n });\n};\n"],"names":["registerSQLCore","knex","createRegisterExtensionPlugin","context","KnexClientFeature"],"mappings":";;AAgBO,MAAMA,kBAAkB,CAAC,EAAEC,IAAI,EAAgC,GAC3DC,8BAA8B,OAAMC;QACvCC,kBAAkB,QAAQ,CAACD,QAAQ,SAAS,EAAEF;IAClD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-core-sql",
3
- "version": "0.0.0-unstable.b6d7105cee",
3
+ "version": "0.0.0-unstable.e9333b3f0b",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -21,18 +21,17 @@
21
21
  "author": "Webiny Ltd.",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@webiny/api-core": "0.0.0-unstable.b6d7105cee",
25
- "@webiny/error": "0.0.0-unstable.b6d7105cee",
26
- "@webiny/feature": "0.0.0-unstable.b6d7105cee",
27
- "@webiny/handler": "0.0.0-unstable.b6d7105cee",
24
+ "@webiny/api-core": "0.0.0-unstable.e9333b3f0b",
25
+ "@webiny/error": "0.0.0-unstable.e9333b3f0b",
26
+ "@webiny/feature": "0.0.0-unstable.e9333b3f0b",
27
+ "@webiny/handler": "0.0.0-unstable.e9333b3f0b",
28
28
  "knex": "3.3.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@webiny/build-tools": "0.0.0-unstable.b6d7105cee",
32
- "@webiny/project-utils": "0.0.0-unstable.b6d7105cee",
33
- "better-sqlite3": "13.0.1",
31
+ "@webiny/build-tools": "0.0.0-unstable.e9333b3f0b",
32
+ "better-sqlite3": "13.0.3",
34
33
  "typescript": "7.0.2",
35
- "vitest": "4.1.10"
34
+ "vitest": "4.1.11"
36
35
  },
37
36
  "publishConfig": {
38
37
  "access": "public"