@undefineds.co/xpod 0.3.24 → 0.3.26

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 (89) hide show
  1. package/config/cloud.json +6 -5
  2. package/config/main.json +0 -3
  3. package/config/xpod.base.json +0 -32
  4. package/dist/api/container/index.js +3 -0
  5. package/dist/api/container/index.js.map +1 -1
  6. package/dist/api/container/routes.js +2 -0
  7. package/dist/api/container/routes.js.map +1 -1
  8. package/dist/api/container/types.d.ts +5 -0
  9. package/dist/api/container/types.js.map +1 -1
  10. package/dist/authorization/AuthMode.d.ts +8 -0
  11. package/dist/authorization/AuthMode.js +51 -0
  12. package/dist/authorization/AuthMode.js.map +1 -0
  13. package/dist/authorization/PodAuthorizationResources.d.ts +18 -0
  14. package/dist/authorization/PodAuthorizationResources.js +108 -0
  15. package/dist/authorization/PodAuthorizationResources.js.map +1 -0
  16. package/dist/cli/commands/start.js +11 -2
  17. package/dist/cli/commands/start.js.map +1 -1
  18. package/dist/components/components.jsonld +3 -2
  19. package/dist/components/context.jsonld +115 -14
  20. package/dist/index.d.ts +5 -3
  21. package/dist/index.js +6 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/main.js +11 -2
  24. package/dist/main.js.map +1 -1
  25. package/dist/provision/LocalPodProvisioningService.d.ts +6 -2
  26. package/dist/provision/LocalPodProvisioningService.js +36 -33
  27. package/dist/provision/LocalPodProvisioningService.js.map +1 -1
  28. package/dist/provision/LocalPodProvisioningService.jsonld +65 -8
  29. package/dist/runtime/XpodRuntime.js +0 -1
  30. package/dist/runtime/XpodRuntime.js.map +1 -1
  31. package/dist/runtime/bootstrap.d.ts +4 -2
  32. package/dist/runtime/bootstrap.js +82 -12
  33. package/dist/runtime/bootstrap.js.map +1 -1
  34. package/dist/runtime/css-process.d.ts +6 -1
  35. package/dist/runtime/css-process.js +55 -7
  36. package/dist/runtime/css-process.js.map +1 -1
  37. package/dist/runtime/lifecycle.d.ts +2 -3
  38. package/dist/runtime/lifecycle.js +2 -2
  39. package/dist/runtime/lifecycle.js.map +1 -1
  40. package/dist/runtime/runtime-types.d.ts +2 -1
  41. package/dist/runtime/runtime-types.js.map +1 -1
  42. package/dist/storage/accessors/SolidRdfDataAccessor.d.ts +2 -3
  43. package/dist/storage/accessors/SolidRdfDataAccessor.js +48 -42
  44. package/dist/storage/accessors/SolidRdfDataAccessor.js.map +1 -1
  45. package/dist/storage/accessors/SolidRdfDataAccessor.jsonld +1 -1
  46. package/dist/storage/keyvalue/BaseKeyValueStorage.d.ts +33 -0
  47. package/dist/storage/keyvalue/BaseKeyValueStorage.js +106 -0
  48. package/dist/storage/keyvalue/BaseKeyValueStorage.js.map +1 -0
  49. package/dist/storage/keyvalue/BaseKeyValueStorage.jsonld +177 -0
  50. package/dist/storage/keyvalue/PostgresKeyValueStorage.d.ts +9 -18
  51. package/dist/storage/keyvalue/PostgresKeyValueStorage.js +24 -96
  52. package/dist/storage/keyvalue/PostgresKeyValueStorage.js.map +1 -1
  53. package/dist/storage/keyvalue/PostgresKeyValueStorage.jsonld +15 -58
  54. package/dist/storage/keyvalue/SqliteKeyValueStorage.d.ts +9 -15
  55. package/dist/storage/keyvalue/SqliteKeyValueStorage.js +36 -104
  56. package/dist/storage/keyvalue/SqliteKeyValueStorage.js.map +1 -1
  57. package/dist/storage/keyvalue/SqliteKeyValueStorage.jsonld +21 -52
  58. package/dist/storage/quint/BaseQuintStore.d.ts +4 -1
  59. package/dist/storage/quint/BaseQuintStore.js +59 -46
  60. package/dist/storage/quint/BaseQuintStore.js.map +1 -1
  61. package/dist/storage/quint/PgQuintStore.d.ts +4 -3
  62. package/dist/storage/quint/PgQuintStore.js.map +1 -1
  63. package/dist/storage/quint/SqliteQuintStore.d.ts +43 -54
  64. package/dist/storage/quint/SqliteQuintStore.js +197 -520
  65. package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
  66. package/dist/storage/quint/SqliteQuintStore.jsonld +38 -86
  67. package/dist/storage/rdf/PostgresRdfEngine.d.ts +118 -0
  68. package/dist/storage/rdf/PostgresRdfEngine.js +2609 -0
  69. package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -0
  70. package/dist/storage/rdf/PostgresRdfEngine.jsonld +657 -0
  71. package/dist/storage/rdf/SolidRdfEngine.d.ts +2 -2
  72. package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
  73. package/dist/storage/rdf/SolidRdfEngine.jsonld +3 -0
  74. package/dist/storage/rdf/SolidRdfSparqlEngine.d.ts +3 -3
  75. package/dist/storage/rdf/SolidRdfSparqlEngine.js +20 -20
  76. package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -1
  77. package/dist/storage/rdf/SolidRdfSparqlEngine.jsonld +1 -1
  78. package/dist/storage/rdf/index.d.ts +2 -1
  79. package/dist/storage/rdf/index.js +3 -1
  80. package/dist/storage/rdf/index.js.map +1 -1
  81. package/dist/storage/rdf/types.d.ts +19 -0
  82. package/dist/storage/rdf/types.js.map +1 -1
  83. package/dist/storage/rdf/types.jsonld +115 -0
  84. package/package.json +2 -2
  85. package/config/runtime-open.json +0 -22
  86. package/dist/authorization/AuthModeSelector.d.ts +0 -10
  87. package/dist/authorization/AuthModeSelector.js +0 -27
  88. package/dist/authorization/AuthModeSelector.js.map +0 -1
  89. package/dist/authorization/AuthModeSelector.jsonld +0 -81
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseKeyValueStorage = void 0;
4
+ const global_logger_factory_1 = require("global-logger-factory");
5
+ class BaseKeyValueStorage {
6
+ constructor(options) {
7
+ this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
8
+ this.ready = Promise.resolve();
9
+ this.tableName = options.tableName ?? 'internal_kv';
10
+ this.namespace = options.namespace ?? '';
11
+ assertIdentifier(this.tableName);
12
+ }
13
+ setReady(ready) {
14
+ this.ready = ready;
15
+ }
16
+ async initialize() {
17
+ await this.ready;
18
+ }
19
+ async finalize() {
20
+ await this.ready;
21
+ await this.closeStorage();
22
+ }
23
+ async has(key) {
24
+ await this.ready;
25
+ return this.hasValue(this.toStorageKey(key));
26
+ }
27
+ async get(key) {
28
+ await this.ready;
29
+ const raw = await this.selectValue(this.toStorageKey(key));
30
+ if (typeof raw === 'undefined') {
31
+ return undefined;
32
+ }
33
+ return this.parseValue(raw);
34
+ }
35
+ async set(key, value) {
36
+ await this.ready;
37
+ const storageKey = this.toStorageKey(key);
38
+ let payload;
39
+ try {
40
+ payload = this.validateAndSerialize(value, key);
41
+ }
42
+ catch (error) {
43
+ this.logger.error(`Failed to serialize value for key "${key}": ${error}`);
44
+ throw error;
45
+ }
46
+ await this.upsertValue(storageKey, payload);
47
+ return this;
48
+ }
49
+ async delete(key) {
50
+ await this.ready;
51
+ return this.deleteValue(this.toStorageKey(key));
52
+ }
53
+ async *entries() {
54
+ await this.ready;
55
+ const prefix = this.namespace;
56
+ const rows = await this.selectEntries(prefix);
57
+ for (const row of rows) {
58
+ if (!row.key.startsWith(prefix)) {
59
+ continue;
60
+ }
61
+ const logicalKey = row.key.slice(prefix.length);
62
+ const value = this.parseValue(row.value);
63
+ if (typeof value === 'undefined') {
64
+ continue;
65
+ }
66
+ yield [logicalKey, value];
67
+ }
68
+ }
69
+ toStorageKey(key) {
70
+ return `${this.namespace}${key}`;
71
+ }
72
+ validateAndSerialize(value, key) {
73
+ try {
74
+ const payload = JSON.stringify(value ?? null);
75
+ if (payload === 'undefined') {
76
+ throw new Error('Cannot serialize undefined value');
77
+ }
78
+ JSON.parse(payload);
79
+ return payload;
80
+ }
81
+ catch (error) {
82
+ this.logger.error(`JSON serialization failed for key "${key}": ${error}`);
83
+ throw new Error(`JSON serialization failed for key "${key}": ${error}`);
84
+ }
85
+ }
86
+ parseValue(raw) {
87
+ try {
88
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
89
+ if (parsed && typeof parsed === 'object' && 'key' in parsed && 'payload' in parsed) {
90
+ return parsed.payload;
91
+ }
92
+ return parsed;
93
+ }
94
+ catch (error) {
95
+ this.logger.error(`Failed to parse stored value: ${error}. Raw value: ${JSON.stringify(raw)}`);
96
+ return undefined;
97
+ }
98
+ }
99
+ }
100
+ exports.BaseKeyValueStorage = BaseKeyValueStorage;
101
+ function assertIdentifier(name) {
102
+ if (!/^[A-Za-z0-9_]+$/u.test(name)) {
103
+ throw new Error(`Invalid identifier: "${name}". Only alphanumeric and underscore are allowed.`);
104
+ }
105
+ }
106
+ //# sourceMappingURL=BaseKeyValueStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseKeyValueStorage.js","sourceRoot":"","sources":["../../../src/storage/keyvalue/BaseKeyValueStorage.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Finalizable,\n Initializable,\n KeyValueStorage,\n} from '@solid/community-server';\nimport { getLoggerFor } from 'global-logger-factory';\n\nexport interface BaseKeyValueStorageOptions {\n tableName?: string;\n namespace?: string;\n}\n\nexport interface BaseKeyValueStorageRow {\n key: string;\n value: unknown;\n}\n\nexport abstract class BaseKeyValueStorage<T = unknown> implements\n KeyValueStorage<string, T>,\n Initializable,\n Finalizable {\n protected readonly logger = getLoggerFor(this);\n protected readonly tableName: string;\n protected readonly namespace: string;\n\n private ready: Promise<void> = Promise.resolve();\n\n protected constructor(options: BaseKeyValueStorageOptions) {\n this.tableName = options.tableName ?? 'internal_kv';\n this.namespace = options.namespace ?? '';\n assertIdentifier(this.tableName);\n }\n\n protected setReady(ready: Promise<void>): void {\n this.ready = ready;\n }\n\n public async initialize(): Promise<void> {\n await this.ready;\n }\n\n public async finalize(): Promise<void> {\n await this.ready;\n await this.closeStorage();\n }\n\n public async has(key: string): Promise<boolean> {\n await this.ready;\n return this.hasValue(this.toStorageKey(key));\n }\n\n public async get(key: string): Promise<T | undefined> {\n await this.ready;\n const raw = await this.selectValue(this.toStorageKey(key));\n if (typeof raw === 'undefined') {\n return undefined;\n }\n return this.parseValue(raw);\n }\n\n public async set(key: string, value: T): Promise<this> {\n await this.ready;\n const storageKey = this.toStorageKey(key);\n\n let payload: string;\n try {\n payload = this.validateAndSerialize(value, key);\n } catch (error: unknown) {\n this.logger.error(`Failed to serialize value for key \"${key}\": ${error}`);\n throw error;\n }\n\n await this.upsertValue(storageKey, payload);\n return this;\n }\n\n public async delete(key: string): Promise<boolean> {\n await this.ready;\n return this.deleteValue(this.toStorageKey(key));\n }\n\n public async *entries(): AsyncIterableIterator<[ string, T ]> {\n await this.ready;\n const prefix = this.namespace;\n const rows = await this.selectEntries(prefix);\n\n for (const row of rows) {\n if (!row.key.startsWith(prefix)) {\n continue;\n }\n\n const logicalKey = row.key.slice(prefix.length);\n const value = this.parseValue(row.value);\n if (typeof value === 'undefined') {\n continue;\n }\n\n yield [ logicalKey, value ];\n }\n }\n\n protected toStorageKey(key: string): string {\n return `${this.namespace}${key}`;\n }\n\n protected validateAndSerialize(value: T, key: string): string {\n try {\n const payload = JSON.stringify(value ?? null);\n if (payload === 'undefined') {\n throw new Error('Cannot serialize undefined value');\n }\n JSON.parse(payload);\n return payload;\n } catch (error: unknown) {\n this.logger.error(`JSON serialization failed for key \"${key}\": ${error}`);\n throw new Error(`JSON serialization failed for key \"${key}\": ${error}`);\n }\n }\n\n protected parseValue(raw: unknown): T | undefined {\n try {\n const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n\n if (parsed && typeof parsed === 'object' && 'key' in parsed && 'payload' in parsed) {\n return (parsed as { payload: T }).payload;\n }\n\n return parsed as T;\n } catch (error: unknown) {\n this.logger.error(`Failed to parse stored value: ${error}. Raw value: ${JSON.stringify(raw)}`);\n return undefined;\n }\n }\n\n protected abstract closeStorage(): Promise<void>;\n protected abstract hasValue(key: string): Promise<boolean>;\n protected abstract selectValue(key: string): Promise<unknown | undefined>;\n protected abstract upsertValue(key: string, payload: string): Promise<void>;\n protected abstract deleteValue(key: string): Promise<boolean>;\n protected abstract selectEntries(prefix: string): Promise<BaseKeyValueStorageRow[]>;\n}\n\nfunction assertIdentifier(name: string): void {\n if (!/^[A-Za-z0-9_]+$/u.test(name)) {\n throw new Error(`Invalid identifier: \"${name}\". Only alphanumeric and underscore are allowed.`);\n }\n}\n"]}
@@ -0,0 +1,177 @@
1
+ {
2
+ "@context": [
3
+ "https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld",
4
+ "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^8.0.0/components/context.jsonld"
5
+ ],
6
+ "@id": "npmd:@undefineds.co/xpod",
7
+ "components": [
8
+ {
9
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage",
10
+ "@type": "AbstractClass",
11
+ "requireElement": "BaseKeyValueStorage",
12
+ "extends": [
13
+ {
14
+ "@type": "GenericComponentExtension",
15
+ "component": "css:dist/storage/keyvalue/KeyValueStorage.jsonld#KeyValueStorage",
16
+ "genericTypeInstances": [
17
+ "xsd:string",
18
+ {
19
+ "@type": "ParameterRangeGenericTypeReference",
20
+ "parameterRangeGenericType": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__generic_T"
21
+ }
22
+ ]
23
+ },
24
+ "css:dist/init/Initializable.jsonld#Initializable",
25
+ "css:dist/init/final/Finalizable.jsonld#Finalizable"
26
+ ],
27
+ "genericTypeParameters": [
28
+ {
29
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__generic_T",
30
+ "default": {
31
+ "@type": "ParameterRangeWildcard"
32
+ }
33
+ }
34
+ ],
35
+ "parameters": [
36
+ {
37
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage_options_tableName",
38
+ "range": {
39
+ "@type": "ParameterRangeUnion",
40
+ "parameterRangeElements": [
41
+ "xsd:string",
42
+ {
43
+ "@type": "ParameterRangeUndefined"
44
+ }
45
+ ]
46
+ }
47
+ },
48
+ {
49
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage_options_namespace",
50
+ "range": {
51
+ "@type": "ParameterRangeUnion",
52
+ "parameterRangeElements": [
53
+ "xsd:string",
54
+ {
55
+ "@type": "ParameterRangeUndefined"
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ ],
61
+ "memberFields": [
62
+ {
63
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_logger",
64
+ "memberFieldName": "logger",
65
+ "range": {
66
+ "@type": "ParameterRangeWildcard"
67
+ }
68
+ },
69
+ {
70
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_tableName",
71
+ "memberFieldName": "tableName",
72
+ "range": "xsd:string"
73
+ },
74
+ {
75
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_namespace",
76
+ "memberFieldName": "namespace",
77
+ "range": "xsd:string"
78
+ },
79
+ {
80
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_ready",
81
+ "memberFieldName": "ready"
82
+ },
83
+ {
84
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_constructor",
85
+ "memberFieldName": "constructor"
86
+ },
87
+ {
88
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_setReady",
89
+ "memberFieldName": "setReady"
90
+ },
91
+ {
92
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_initialize",
93
+ "memberFieldName": "initialize"
94
+ },
95
+ {
96
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_finalize",
97
+ "memberFieldName": "finalize"
98
+ },
99
+ {
100
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_has",
101
+ "memberFieldName": "has"
102
+ },
103
+ {
104
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_get",
105
+ "memberFieldName": "get"
106
+ },
107
+ {
108
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_set",
109
+ "memberFieldName": "set"
110
+ },
111
+ {
112
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_delete",
113
+ "memberFieldName": "delete"
114
+ },
115
+ {
116
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_entries",
117
+ "memberFieldName": "entries"
118
+ },
119
+ {
120
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_toStorageKey",
121
+ "memberFieldName": "toStorageKey"
122
+ },
123
+ {
124
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_validateAndSerialize",
125
+ "memberFieldName": "validateAndSerialize"
126
+ },
127
+ {
128
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_parseValue",
129
+ "memberFieldName": "parseValue"
130
+ },
131
+ {
132
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_closeStorage",
133
+ "memberFieldName": "closeStorage"
134
+ },
135
+ {
136
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_hasValue",
137
+ "memberFieldName": "hasValue"
138
+ },
139
+ {
140
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_selectValue",
141
+ "memberFieldName": "selectValue"
142
+ },
143
+ {
144
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_upsertValue",
145
+ "memberFieldName": "upsertValue"
146
+ },
147
+ {
148
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_deleteValue",
149
+ "memberFieldName": "deleteValue"
150
+ },
151
+ {
152
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage__member_selectEntries",
153
+ "memberFieldName": "selectEntries"
154
+ }
155
+ ],
156
+ "constructorArguments": [
157
+ {
158
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage_options__constructorArgument",
159
+ "fields": [
160
+ {
161
+ "keyRaw": "tableName",
162
+ "value": {
163
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage_options_tableName"
164
+ }
165
+ },
166
+ {
167
+ "keyRaw": "namespace",
168
+ "value": {
169
+ "@id": "undefineds:dist/storage/keyvalue/BaseKeyValueStorage.jsonld#BaseKeyValueStorage_options_namespace"
170
+ }
171
+ }
172
+ ]
173
+ }
174
+ ]
175
+ }
176
+ ]
177
+ }
@@ -1,5 +1,5 @@
1
1
  import { Pool } from 'pg';
2
- import type { Finalizable, Initializable, KeyValueStorage } from '@solid/community-server';
2
+ import { BaseKeyValueStorage, type BaseKeyValueStorageRow } from './BaseKeyValueStorage';
3
3
  export interface PostgresKeyValueStorageOptions {
4
4
  connectionString: string;
5
5
  tableName?: string;
@@ -10,25 +10,16 @@ export interface PostgresKeyValueStorageOptions {
10
10
  */
11
11
  pool?: Pool;
12
12
  }
13
- export declare class PostgresKeyValueStorage<T = unknown> implements KeyValueStorage<string, T>, Initializable, Finalizable {
14
- protected readonly logger: import("global-logger-factory").Logger<unknown>;
13
+ export declare class PostgresKeyValueStorage<T = unknown> extends BaseKeyValueStorage<T> {
15
14
  private readonly pool;
16
- private readonly tableName;
17
15
  private readonly quotedTableName;
18
- private readonly namespace;
19
- private readonly ready;
20
16
  private readonly sharedConnectionString?;
21
17
  constructor(options: PostgresKeyValueStorageOptions);
22
- initialize(): Promise<void>;
23
- finalize(): Promise<void>;
24
- has(key: string): Promise<boolean>;
25
- get(key: string): Promise<T | undefined>;
26
- set(key: string, value: T): Promise<this>;
27
- delete(key: string): Promise<boolean>;
28
- entries(): AsyncIterableIterator<[string, T]>;
29
- protected ensureTable(): Promise<void>;
30
- protected toStorageKey(key: string): string;
31
- protected formatIdentifier(name: string): string;
32
- protected validateAndSerialize(value: T, key: string): string;
33
- protected parseValue(raw: any): T | undefined;
18
+ protected closeStorage(): Promise<void>;
19
+ protected hasValue(key: string): Promise<boolean>;
20
+ protected selectValue(key: string): Promise<unknown | undefined>;
21
+ protected upsertValue(key: string, payload: string): Promise<void>;
22
+ protected deleteValue(key: string): Promise<boolean>;
23
+ protected selectEntries(prefix: string): Promise<BaseKeyValueStorageRow[]>;
24
+ private ensureTable;
34
25
  }
@@ -1,17 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PostgresKeyValueStorage = void 0;
4
- const global_logger_factory_1 = require("global-logger-factory");
5
4
  const PostgresPoolManager_1 = require("../database/PostgresPoolManager");
6
- function assertIdentifier(name) {
7
- if (!/^[A-Za-z0-9_]+$/u.test(name)) {
8
- throw new Error(`Invalid identifier: "${name}". Only alphanumeric and underscore are allowed.`);
9
- }
10
- }
11
- class PostgresKeyValueStorage {
5
+ const BaseKeyValueStorage_1 = require("./BaseKeyValueStorage");
6
+ class PostgresKeyValueStorage extends BaseKeyValueStorage_1.BaseKeyValueStorage {
12
7
  constructor(options) {
13
- this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
14
- // 使用共享的连接池,避免多个组件创建独立连接池导致死锁
8
+ super(options);
15
9
  if (options.pool) {
16
10
  this.pool = options.pool;
17
11
  this.sharedConnectionString = undefined;
@@ -20,78 +14,43 @@ class PostgresKeyValueStorage {
20
14
  this.sharedConnectionString = options.connectionString;
21
15
  this.pool = (0, PostgresPoolManager_1.getSharedPool)({ connectionString: options.connectionString });
22
16
  }
23
- this.tableName = options.tableName ?? 'internal_kv';
24
- this.namespace = options.namespace ?? '';
25
- assertIdentifier(this.tableName);
26
- this.quotedTableName = this.formatIdentifier(this.tableName);
27
- this.ready = this.ensureTable();
28
- }
29
- async initialize() {
30
- await this.ready;
17
+ const tableName = options.tableName ?? 'internal_kv';
18
+ this.quotedTableName = formatIdentifier(tableName);
19
+ this.setReady(this.ensureTable());
31
20
  }
32
- async finalize() {
21
+ async closeStorage() {
33
22
  if (!this.sharedConnectionString) {
34
23
  return;
35
24
  }
36
25
  (0, PostgresPoolManager_1.releaseSharedPool)({ connectionString: this.sharedConnectionString });
37
26
  }
38
- async has(key) {
39
- await this.ready;
40
- const storageKey = this.toStorageKey(key);
41
- const result = await this.pool.query(`SELECT EXISTS (SELECT 1 FROM ${this.quotedTableName} WHERE key = $1) AS exists`, [storageKey]);
27
+ async hasValue(key) {
28
+ const result = await this.pool.query(`SELECT EXISTS (SELECT 1 FROM ${this.quotedTableName} WHERE key = $1) AS exists`, [key]);
42
29
  return result.rows[0]?.exists ?? false;
43
30
  }
44
- async get(key) {
45
- await this.ready;
46
- const storageKey = this.toStorageKey(key);
47
- const result = await this.pool.query(`SELECT value FROM ${this.quotedTableName} WHERE key = $1 LIMIT 1`, [storageKey]);
31
+ async selectValue(key) {
32
+ const result = await this.pool.query(`SELECT value FROM ${this.quotedTableName} WHERE key = $1 LIMIT 1`, [key]);
48
33
  if ((result.rowCount ?? 0) === 0) {
49
34
  return undefined;
50
35
  }
51
- // JSONB column returns object, TEXT column returns string
52
- return this.parseValue(result.rows[0].value);
36
+ return result.rows[0]?.value;
53
37
  }
54
- async set(key, value) {
55
- await this.ready;
56
- const storageKey = this.toStorageKey(key);
57
- let payload;
58
- try {
59
- payload = this.validateAndSerialize(value, key);
60
- }
61
- catch (error) {
62
- this.logger.error(`Failed to serialize value for key "${key}": ${error}`);
63
- throw error;
64
- }
38
+ async upsertValue(key, payload) {
65
39
  await this.pool.query(`INSERT INTO ${this.quotedTableName} (key, value)
66
40
  VALUES ($1, $2)
67
- ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`, [storageKey, payload]);
68
- return this;
41
+ ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`, [key, payload]);
69
42
  }
70
- async delete(key) {
71
- await this.ready;
72
- const storageKey = this.toStorageKey(key);
73
- const result = await this.pool.query(`DELETE FROM ${this.quotedTableName} WHERE key = $1`, [storageKey]);
43
+ async deleteValue(key) {
44
+ const result = await this.pool.query(`DELETE FROM ${this.quotedTableName} WHERE key = $1`, [key]);
74
45
  return (result.rowCount ?? 0) > 0;
75
46
  }
76
- async *entries() {
77
- await this.ready;
78
- const prefix = this.namespace;
47
+ async selectEntries(prefix) {
79
48
  const query = prefix.length > 0 ?
80
49
  `SELECT key, value FROM ${this.quotedTableName} WHERE key LIKE $1` :
81
50
  `SELECT key, value FROM ${this.quotedTableName}`;
82
51
  const values = prefix.length > 0 ? [`${prefix}%`] : [];
83
52
  const result = await this.pool.query(query, values);
84
- for (const row of result.rows) {
85
- if (!row.key.startsWith(prefix)) {
86
- continue;
87
- }
88
- const logicalKey = row.key.slice(prefix.length);
89
- const value = this.parseValue(row.value);
90
- if (typeof value === 'undefined') {
91
- continue;
92
- }
93
- yield [logicalKey, value];
94
- }
53
+ return result.rows;
95
54
  }
96
55
  async ensureTable() {
97
56
  await this.pool.query(`
@@ -102,43 +61,12 @@ class PostgresKeyValueStorage {
102
61
  )
103
62
  `);
104
63
  }
105
- toStorageKey(key) {
106
- return `${this.namespace}${key}`;
107
- }
108
- formatIdentifier(name) {
109
- return `"${name}"`;
110
- }
111
- validateAndSerialize(value, key) {
112
- try {
113
- const payload = JSON.stringify(value ?? null);
114
- // Basic validation
115
- if (payload === 'undefined') {
116
- throw new Error(`Cannot serialize undefined value`);
117
- }
118
- // Validate JSON can be parsed back
119
- JSON.parse(payload);
120
- return payload;
121
- }
122
- catch (error) {
123
- this.logger.error(`JSON serialization failed for key "${key}": ${error}`);
124
- throw new Error(`JSON serialization failed for key "${key}": ${error}`);
125
- }
126
- }
127
- parseValue(raw) {
128
- try {
129
- // JSONB column returns object, TEXT column returns string
130
- const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
131
- // Handle CSS internal storage format: {"key": "...", "payload": ...}
132
- if (parsed && typeof parsed === 'object' && 'key' in parsed && 'payload' in parsed) {
133
- return parsed.payload;
134
- }
135
- return parsed;
136
- }
137
- catch (error) {
138
- this.logger.error(`Failed to parse stored value: ${error}. Raw value: ${JSON.stringify(raw)}`);
139
- return undefined;
140
- }
141
- }
142
64
  }
143
65
  exports.PostgresKeyValueStorage = PostgresKeyValueStorage;
66
+ function formatIdentifier(name) {
67
+ if (!/^[A-Za-z0-9_]+$/u.test(name)) {
68
+ throw new Error(`Invalid identifier: "${name}". Only alphanumeric and underscore are allowed.`);
69
+ }
70
+ return `"${name}"`;
71
+ }
144
72
  //# sourceMappingURL=PostgresKeyValueStorage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresKeyValueStorage.js","sourceRoot":"","sources":["../../../src/storage/keyvalue/PostgresKeyValueStorage.ts"],"names":[],"mappings":"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","sourcesContent":["import { Pool } from 'pg';\nimport type {\n Finalizable,\n Initializable,\n KeyValueStorage,\n} from '@solid/community-server';\nimport { getLoggerFor } from 'global-logger-factory';\nimport { getSharedPool, releaseSharedPool } from '../database/PostgresPoolManager';\n\nexport interface PostgresKeyValueStorageOptions {\n connectionString: string;\n tableName?: string;\n namespace?: string;\n /** \n * 共享的 pg Pool 实例(避免多个组件创建独立连接池导致死锁)\n * 如果提供,将忽略 connectionString\n */\n pool?: Pool;\n}\n\nfunction assertIdentifier(name: string): void {\n if (!/^[A-Za-z0-9_]+$/u.test(name)) {\n throw new Error(`Invalid identifier: \"${name}\". Only alphanumeric and underscore are allowed.`);\n }\n}\n\nexport class PostgresKeyValueStorage<T = unknown> implements\n KeyValueStorage<string, T>,\n Initializable,\n Finalizable {\n protected readonly logger = getLoggerFor(this);\n private readonly pool: Pool;\n private readonly tableName: string;\n private readonly quotedTableName: string;\n private readonly namespace: string;\n private readonly ready: Promise<void>;\n private readonly sharedConnectionString?: string;\n\n public constructor(options: PostgresKeyValueStorageOptions) {\n // 使用共享的连接池,避免多个组件创建独立连接池导致死锁\n if (options.pool) {\n this.pool = options.pool;\n this.sharedConnectionString = undefined;\n } else {\n this.sharedConnectionString = options.connectionString;\n this.pool = getSharedPool({ connectionString: options.connectionString });\n }\n this.tableName = options.tableName ?? 'internal_kv';\n this.namespace = options.namespace ?? '';\n assertIdentifier(this.tableName);\n this.quotedTableName = this.formatIdentifier(this.tableName);\n this.ready = this.ensureTable();\n }\n\n public async initialize(): Promise<void> {\n await this.ready;\n }\n\n public async finalize(): Promise<void> {\n if (!this.sharedConnectionString) {\n return;\n }\n releaseSharedPool({ connectionString: this.sharedConnectionString });\n }\n\n\n public async has(key: string): Promise<boolean> {\n await this.ready;\n const storageKey = this.toStorageKey(key);\n const result = await this.pool.query<{ exists: boolean }>(\n `SELECT EXISTS (SELECT 1 FROM ${this.quotedTableName} WHERE key = $1) AS exists`,\n [ storageKey ],\n );\n return result.rows[0]?.exists ?? false;\n }\n\n public async get(key: string): Promise<T | undefined> {\n await this.ready;\n const storageKey = this.toStorageKey(key);\n const result = await this.pool.query<{ value: any }>(\n `SELECT value FROM ${this.quotedTableName} WHERE key = $1 LIMIT 1`,\n [ storageKey ],\n );\n if ((result.rowCount ?? 0) === 0) {\n return undefined;\n }\n // JSONB column returns object, TEXT column returns string\n return this.parseValue(result.rows[0].value);\n }\n\n public async set(key: string, value: T): Promise<this> {\n await this.ready;\n const storageKey = this.toStorageKey(key);\n \n let payload: string;\n try {\n payload = this.validateAndSerialize(value, key);\n } catch (error: unknown) {\n this.logger.error(`Failed to serialize value for key \"${key}\": ${error}`);\n throw error;\n }\n \n await this.pool.query(\n `INSERT INTO ${this.quotedTableName} (key, value)\n VALUES ($1, $2)\n ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`,\n [ storageKey, payload ],\n );\n return this;\n }\n\n public async delete(key: string): Promise<boolean> {\n await this.ready;\n const storageKey = this.toStorageKey(key);\n const result = await this.pool.query(\n `DELETE FROM ${this.quotedTableName} WHERE key = $1`,\n [ storageKey ],\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n public async *entries(): AsyncIterableIterator<[ string, T ]> {\n await this.ready;\n const prefix = this.namespace;\n const query = prefix.length > 0 ?\n `SELECT key, value FROM ${this.quotedTableName} WHERE key LIKE $1` :\n `SELECT key, value FROM ${this.quotedTableName}`;\n const values = prefix.length > 0 ? [ `${prefix}%` ] : [];\n const result = await this.pool.query<{ key: string; value: any }>(query, values);\n for (const row of result.rows) {\n if (!row.key.startsWith(prefix)) {\n continue;\n }\n const logicalKey = row.key.slice(prefix.length);\n const value = this.parseValue(row.value);\n if (typeof value === 'undefined') {\n continue;\n }\n yield [ logicalKey, value ];\n }\n }\n\n protected async ensureTable(): Promise<void> {\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.quotedTableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n )\n `);\n }\n\n protected toStorageKey(key: string): string {\n return `${this.namespace}${key}`;\n }\n\n protected formatIdentifier(name: string): string {\n return `\"${name}\"`;\n }\n\n protected validateAndSerialize(value: T, key: string): string {\n try {\n const payload = JSON.stringify(value ?? null);\n \n // Basic validation\n if (payload === 'undefined') {\n throw new Error(`Cannot serialize undefined value`);\n }\n \n // Validate JSON can be parsed back\n JSON.parse(payload);\n \n return payload;\n } catch (error: unknown) {\n this.logger.error(`JSON serialization failed for key \"${key}\": ${error}`);\n throw new Error(`JSON serialization failed for key \"${key}\": ${error}`);\n }\n }\n\n protected parseValue(raw: any): T | undefined {\n try {\n // JSONB column returns object, TEXT column returns string\n const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n \n // Handle CSS internal storage format: {\"key\": \"...\", \"payload\": ...}\n if (parsed && typeof parsed === 'object' && 'key' in parsed && 'payload' in parsed) {\n return parsed.payload as T;\n }\n \n return parsed as T;\n } catch (error: unknown) {\n this.logger.error(`Failed to parse stored value: ${error}. Raw value: ${JSON.stringify(raw)}`);\n return undefined;\n }\n }\n\n}\n"]}
1
+ {"version":3,"file":"PostgresKeyValueStorage.js","sourceRoot":"","sources":["../../../src/storage/keyvalue/PostgresKeyValueStorage.ts"],"names":[],"mappings":"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","sourcesContent":["import { Pool } from 'pg';\nimport { getSharedPool, releaseSharedPool } from '../database/PostgresPoolManager';\nimport { BaseKeyValueStorage, type BaseKeyValueStorageRow } from './BaseKeyValueStorage';\n\nexport interface PostgresKeyValueStorageOptions {\n connectionString: string;\n tableName?: string;\n namespace?: string;\n /** \n * 共享的 pg Pool 实例(避免多个组件创建独立连接池导致死锁)\n * 如果提供,将忽略 connectionString\n */\n pool?: Pool;\n}\n\nexport class PostgresKeyValueStorage<T = unknown> extends BaseKeyValueStorage<T> {\n private readonly pool: Pool;\n private readonly quotedTableName: string;\n private readonly sharedConnectionString?: string;\n\n public constructor(options: PostgresKeyValueStorageOptions) {\n super(options);\n\n if (options.pool) {\n this.pool = options.pool;\n this.sharedConnectionString = undefined;\n } else {\n this.sharedConnectionString = options.connectionString;\n this.pool = getSharedPool({ connectionString: options.connectionString });\n }\n\n const tableName = options.tableName ?? 'internal_kv';\n this.quotedTableName = formatIdentifier(tableName);\n this.setReady(this.ensureTable());\n }\n\n protected async closeStorage(): Promise<void> {\n if (!this.sharedConnectionString) {\n return;\n }\n releaseSharedPool({ connectionString: this.sharedConnectionString });\n }\n\n protected async hasValue(key: string): Promise<boolean> {\n const result = await this.pool.query<{ exists: boolean }>(\n `SELECT EXISTS (SELECT 1 FROM ${this.quotedTableName} WHERE key = $1) AS exists`,\n [ key ],\n );\n return result.rows[0]?.exists ?? false;\n }\n\n protected async selectValue(key: string): Promise<unknown | undefined> {\n const result = await this.pool.query<{ value: unknown }>(\n `SELECT value FROM ${this.quotedTableName} WHERE key = $1 LIMIT 1`,\n [ key ],\n );\n if ((result.rowCount ?? 0) === 0) {\n return undefined;\n }\n return result.rows[0]?.value;\n }\n\n protected async upsertValue(key: string, payload: string): Promise<void> {\n await this.pool.query(\n `INSERT INTO ${this.quotedTableName} (key, value)\n VALUES ($1, $2)\n ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()`,\n [ key, payload ],\n );\n }\n\n protected async deleteValue(key: string): Promise<boolean> {\n const result = await this.pool.query(\n `DELETE FROM ${this.quotedTableName} WHERE key = $1`,\n [ key ],\n );\n return (result.rowCount ?? 0) > 0;\n }\n\n protected async selectEntries(prefix: string): Promise<BaseKeyValueStorageRow[]> {\n const query = prefix.length > 0 ?\n `SELECT key, value FROM ${this.quotedTableName} WHERE key LIKE $1` :\n `SELECT key, value FROM ${this.quotedTableName}`;\n const values = prefix.length > 0 ? [ `${prefix}%` ] : [];\n const result = await this.pool.query<BaseKeyValueStorageRow>(query, values);\n return result.rows;\n }\n\n private async ensureTable(): Promise<void> {\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.quotedTableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n )\n `);\n }\n}\n\nfunction formatIdentifier(name: string): string {\n if (!/^[A-Za-z0-9_]+$/u.test(name)) {\n throw new Error(`Invalid identifier: \"${name}\". Only alphanumeric and underscore are allowed.`);\n }\n return `\"${name}\"`;\n}\n"]}