@peers-app/peers-sdk 0.23.4 → 0.23.6
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/dist/context/data-context.d.ts +36 -5
- package/dist/context/data-context.js +44 -9
- package/dist/context/user-context.d.ts +44 -0
- package/dist/context/user-context.js +91 -12
- package/dist/contracts/__tests__/contract-provider-router.test.js +26 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +44 -0
- package/dist/contracts/__tests__/registry.test.js +104 -0
- package/dist/contracts/contract-provider-router.d.ts +21 -0
- package/dist/contracts/contract-provider-router.js +31 -5
- package/dist/contracts/contract-proxy.d.ts +3 -0
- package/dist/contracts/contract-proxy.js +3 -0
- package/dist/contracts/contract-resolvers.d.ts +21 -3
- package/dist/contracts/contract-resolvers.js +105 -32
- package/dist/contracts/index.d.ts +3 -3
- package/dist/contracts/registry.d.ts +59 -0
- package/dist/contracts/registry.js +155 -25
- package/dist/contracts/system-contracts.js +3 -1
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/orm/types.d.ts +19 -0
- package/dist/data/orm/types.js +11 -0
- package/dist/data/package-table-identity.d.ts +46 -0
- package/dist/data/package-table-identity.js +71 -0
- package/dist/data/package-table-identity.test.d.ts +1 -0
- package/dist/data/package-table-identity.test.js +46 -0
- package/dist/data/package-version-resolver.d.ts +13 -0
- package/dist/data/package-version-resolver.js +142 -6
- package/dist/data/package-version-resolver.test.js +42 -0
- package/dist/data/persistent-var-identity.d.ts +37 -0
- package/dist/data/persistent-var-identity.js +69 -0
- package/dist/data/persistent-vars.d.ts +3 -5
- package/dist/data/persistent-vars.js +25 -13
- package/dist/data/persistent-vars.package-scope.test.d.ts +1 -0
- package/dist/data/persistent-vars.package-scope.test.js +27 -0
- package/dist/data/tools.d.ts +3 -3
- package/dist/package-installer/index.d.ts +1 -0
- package/dist/package-installer/index.js +1 -0
- package/dist/package-installer/package-cloner.d.ts +1 -1
- package/dist/package-installer/package-cloner.js +11 -16
- package/dist/package-installer/package-cloner.test.js +5 -3
- package/dist/package-installer/package-creator.d.ts +4 -3
- package/dist/package-installer/package-creator.js +13 -5
- package/dist/package-installer/package-creator.test.js +19 -0
- package/dist/package-installer/package-dev-tools.d.ts +30 -0
- package/dist/package-installer/package-dev-tools.js +63 -0
- package/dist/package-installer/package-dev-tools.test.d.ts +1 -0
- package/dist/package-installer/package-dev-tools.test.js +64 -0
- package/dist/package-loader/contract-package-loader.js +20 -8
- package/dist/package-loader/contract-package-loader.test.js +16 -0
- package/dist/package-loader/index.d.ts +4 -0
- package/dist/package-loader/index.js +4 -0
- package/dist/package-loader/isolated-artifact.d.ts +233 -0
- package/dist/package-loader/isolated-artifact.js +643 -0
- package/dist/package-loader/isolated-artifact.test.d.ts +1 -0
- package/dist/package-loader/isolated-artifact.test.js +270 -0
- package/dist/package-loader/isolated-persistent-var-gateway.d.ts +20 -0
- package/dist/package-loader/isolated-persistent-var-gateway.js +106 -0
- package/dist/package-loader/isolated-persistent-var-gateway.test.d.ts +1 -0
- package/dist/package-loader/isolated-persistent-var-gateway.test.js +56 -0
- package/dist/package-loader/isolated-runtime.d.ts +229 -0
- package/dist/package-loader/isolated-runtime.js +37 -0
- package/dist/package-loader/isolated-table-gateway.d.ts +16 -0
- package/dist/package-loader/isolated-table-gateway.js +375 -0
- package/dist/package-loader/isolated-table-gateway.test.d.ts +1 -0
- package/dist/package-loader/isolated-table-gateway.test.js +206 -0
- package/dist/package-loader/isolated-value.d.ts +14 -0
- package/dist/package-loader/isolated-value.js +63 -0
- package/dist/package-loader/package-loader.d.ts +123 -4
- package/dist/package-loader/package-loader.js +417 -46
- package/dist/package-loader/package-loader.test.js +478 -12
- package/dist/package-loader/resolve-consumed-contracts.d.ts +3 -3
- package/dist/package-loader/resolve-consumed-contracts.js +18 -10
- package/dist/utils.d.ts +21 -2
- package/dist/utils.js +46 -7
- package/package.json +1 -1
|
@@ -31,26 +31,139 @@ class ContractRegistry {
|
|
|
31
31
|
* @returns Validation result. If invalid the provider is not registered.
|
|
32
32
|
*/
|
|
33
33
|
register(providerPackageId, definition, alsoImplementsDecls = []) {
|
|
34
|
+
return this.registerBatch(providerPackageId, [
|
|
35
|
+
{ definition, alsoImplements: alsoImplementsDecls },
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Atomically register every contract supplied by one provider package.
|
|
40
|
+
*
|
|
41
|
+
* Frozen-shape and `alsoImplements` checks run against a candidate registry
|
|
42
|
+
* containing the whole batch, so declarations may reference another
|
|
43
|
+
* contract in the same install. If any entry fails, no definition, provider,
|
|
44
|
+
* active-provider pointer, or reverse index is changed.
|
|
45
|
+
*
|
|
46
|
+
* @param providerPackageId - Trusted identity of the provider package.
|
|
47
|
+
* @param registrations - Contract definitions and compatibility declarations.
|
|
48
|
+
* @returns The combined validation result.
|
|
49
|
+
*/
|
|
50
|
+
registerBatch(providerPackageId, registrations) {
|
|
51
|
+
const validation = this.validateBatch(providerPackageId, registrations);
|
|
52
|
+
if (!validation.valid) {
|
|
53
|
+
return validation;
|
|
54
|
+
}
|
|
55
|
+
this.commitRegistrations(providerPackageId, registrations);
|
|
56
|
+
return validation;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Atomically replace the complete contract set for one provider package.
|
|
60
|
+
*
|
|
61
|
+
* Validation runs before the existing provider entries are touched. Active
|
|
62
|
+
* selections remain on this package for contract versions it still provides;
|
|
63
|
+
* removed versions fall back according to ordinary unregister semantics.
|
|
64
|
+
*
|
|
65
|
+
* @param providerPackageId - Trusted provider package identity.
|
|
66
|
+
* @param registrations - Complete replacement set.
|
|
67
|
+
* @returns Validation result; invalid replacements leave the registry unchanged.
|
|
68
|
+
*/
|
|
69
|
+
replaceBatch(providerPackageId, registrations) {
|
|
70
|
+
const validation = this.validateBatch(providerPackageId, registrations);
|
|
71
|
+
if (!validation.valid) {
|
|
72
|
+
return validation;
|
|
73
|
+
}
|
|
74
|
+
const previouslyActive = new Set((this.packageContracts.get(providerPackageId) ?? []).filter((key) => this.activeProviders.get(key)?.providerPackageId === providerPackageId));
|
|
75
|
+
this.unregister(providerPackageId);
|
|
76
|
+
this.commitRegistrations(providerPackageId, registrations);
|
|
77
|
+
for (const key of previouslyActive) {
|
|
78
|
+
const provider = this.allProviders
|
|
79
|
+
.get(key)
|
|
80
|
+
?.find((item) => item.providerPackageId === providerPackageId);
|
|
81
|
+
if (provider) {
|
|
82
|
+
this.activeProviders.set(key, provider);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return validation;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate a complete provider registration batch without changing registry state.
|
|
89
|
+
*
|
|
90
|
+
* @param providerPackageId - Trusted provider package identity.
|
|
91
|
+
* @param registrations - Definitions and compatibility declarations to validate.
|
|
92
|
+
* @returns Combined validation result.
|
|
93
|
+
*/
|
|
94
|
+
validateBatch(providerPackageId, registrations) {
|
|
95
|
+
const candidateDefinitions = new Map(this.definitions);
|
|
96
|
+
const batchKeys = new Set();
|
|
97
|
+
for (const { definition } of registrations) {
|
|
98
|
+
const key = (0, types_1.contractKey)(definition.contractId, definition.version);
|
|
99
|
+
if (batchKeys.has(key)) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
errors: [
|
|
103
|
+
{
|
|
104
|
+
kind: "contract",
|
|
105
|
+
name: key,
|
|
106
|
+
message: `Provider ${providerPackageId} registered contract ${key} more than once`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
batchKeys.add(key);
|
|
112
|
+
const existingDef = this.definitions.get(key);
|
|
113
|
+
if (existingDef) {
|
|
114
|
+
const immutability = (0, validate_1.validateImmutability)(existingDef, definition);
|
|
115
|
+
if (!immutability.valid) {
|
|
116
|
+
return immutability;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
candidateDefinitions.set(key, definition);
|
|
120
|
+
}
|
|
121
|
+
for (const { definition, alsoImplements = [] } of registrations) {
|
|
122
|
+
if (alsoImplements.length === 0) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const compatibility = (0, validate_1.validateAlsoImplements)(definition, alsoImplements, (contractId, version) => candidateDefinitions.get((0, types_1.contractKey)(contractId, version)));
|
|
126
|
+
if (!compatibility.valid) {
|
|
127
|
+
return compatibility;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { valid: true, errors: [] };
|
|
131
|
+
}
|
|
132
|
+
commitRegistrations(providerPackageId, registrations) {
|
|
133
|
+
const explicitKeys = new Set(registrations.map(({ definition }) => (0, types_1.contractKey)(definition.contractId, definition.version)));
|
|
134
|
+
for (const { definition } of registrations) {
|
|
135
|
+
this.commitProvider(providerPackageId, definition, definition);
|
|
136
|
+
}
|
|
137
|
+
for (const { definition, alsoImplements = [] } of registrations) {
|
|
138
|
+
for (const declaration of alsoImplements) {
|
|
139
|
+
const declaredVersion = declaration.version;
|
|
140
|
+
const versions = typeof declaredVersion === "number"
|
|
141
|
+
? [declaredVersion]
|
|
142
|
+
: Array.from({ length: declaredVersion.to - declaredVersion.from + 1 }, (_, index) => declaredVersion.from + index);
|
|
143
|
+
for (const version of versions) {
|
|
144
|
+
const key = (0, types_1.contractKey)(declaration.contractId, version);
|
|
145
|
+
if (explicitKeys.has(key)) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const target = this.definitions.get(key);
|
|
149
|
+
if (target) {
|
|
150
|
+
this.commitProvider(providerPackageId, target, definition);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
commitProvider(providerPackageId, definition, implementation) {
|
|
34
157
|
const key = (0, types_1.contractKey)(definition.contractId, definition.version);
|
|
35
|
-
// Immutability check against any previously stored definition
|
|
36
|
-
const existingDef = this.definitions.get(key);
|
|
37
|
-
if (existingDef) {
|
|
38
|
-
const immResult = (0, validate_1.validateImmutability)(existingDef, definition);
|
|
39
|
-
if (!immResult.valid)
|
|
40
|
-
return immResult;
|
|
41
|
-
}
|
|
42
|
-
// alsoImplements check
|
|
43
|
-
if (alsoImplementsDecls.length > 0) {
|
|
44
|
-
const aiResult = (0, validate_1.validateAlsoImplements)(definition, alsoImplementsDecls, (cid, ver) => this.definitions.get((0, types_1.contractKey)(cid, ver)));
|
|
45
|
-
if (!aiResult.valid)
|
|
46
|
-
return aiResult;
|
|
47
|
-
}
|
|
48
|
-
// Store the definition (first-come for frozen, latest for dev)
|
|
49
158
|
this.definitions.set(key, definition);
|
|
50
|
-
const provider = {
|
|
51
|
-
|
|
159
|
+
const provider = {
|
|
160
|
+
providerPackageId,
|
|
161
|
+
definition,
|
|
162
|
+
implementationContractId: implementation.contractId,
|
|
163
|
+
implementationVersion: implementation.version,
|
|
164
|
+
};
|
|
52
165
|
const providers = this.allProviders.get(key) ?? [];
|
|
53
|
-
const existingIdx = providers.findIndex((
|
|
166
|
+
const existingIdx = providers.findIndex((item) => item.providerPackageId === providerPackageId);
|
|
54
167
|
if (existingIdx >= 0) {
|
|
55
168
|
providers[existingIdx] = provider;
|
|
56
169
|
}
|
|
@@ -58,17 +171,15 @@ class ContractRegistry {
|
|
|
58
171
|
providers.push(provider);
|
|
59
172
|
}
|
|
60
173
|
this.allProviders.set(key, providers);
|
|
61
|
-
|
|
62
|
-
if (!
|
|
174
|
+
const active = this.activeProviders.get(key);
|
|
175
|
+
if (!active || active.providerPackageId === providerPackageId) {
|
|
63
176
|
this.activeProviders.set(key, provider);
|
|
64
177
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
pkgKeys.push(key);
|
|
178
|
+
const packageKeys = this.packageContracts.get(providerPackageId) ?? [];
|
|
179
|
+
if (!packageKeys.includes(key)) {
|
|
180
|
+
packageKeys.push(key);
|
|
69
181
|
}
|
|
70
|
-
this.packageContracts.set(providerPackageId,
|
|
71
|
-
return { valid: true, errors: [] };
|
|
182
|
+
this.packageContracts.set(providerPackageId, packageKeys);
|
|
72
183
|
}
|
|
73
184
|
/**
|
|
74
185
|
* Switch the active provider for a contract to a different registered package.
|
|
@@ -106,6 +217,25 @@ class ContractRegistry {
|
|
|
106
217
|
const key = (0, types_1.contractKey)(contractId, version);
|
|
107
218
|
return (this.allProviders.get(key) ?? []).map((p) => p.providerPackageId);
|
|
108
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Resolve the concrete contract implementation selected for one provider.
|
|
222
|
+
*
|
|
223
|
+
* For a direct registration this matches the requested contract. For an
|
|
224
|
+
* `alsoImplements` alias it identifies the newer compatible implementation.
|
|
225
|
+
*
|
|
226
|
+
* @param contractId - Consumer-requested contract identity.
|
|
227
|
+
* @param version - Consumer-requested contract version.
|
|
228
|
+
* @param providerPackageId - Registry-selected provider package.
|
|
229
|
+
*/
|
|
230
|
+
getProviderImplementation(contractId, version, providerPackageId) {
|
|
231
|
+
const provider = (this.allProviders.get((0, types_1.contractKey)(contractId, version)) ?? []).find((candidate) => candidate.providerPackageId === providerPackageId);
|
|
232
|
+
return provider
|
|
233
|
+
? {
|
|
234
|
+
contractId: provider.implementationContractId,
|
|
235
|
+
version: provider.implementationVersion,
|
|
236
|
+
}
|
|
237
|
+
: undefined;
|
|
238
|
+
}
|
|
109
239
|
/**
|
|
110
240
|
* Pick a provider for a consumed-contract declaration.
|
|
111
241
|
*
|
|
@@ -25,7 +25,9 @@ function registerSystemContract(registration) {
|
|
|
25
25
|
const { contractId, version, resolve } = registration;
|
|
26
26
|
queueMicrotask(() => {
|
|
27
27
|
const { registerContractResolver } = require("./contract-resolvers");
|
|
28
|
-
registerContractResolver(contractId, version, resolve
|
|
28
|
+
registerContractResolver(contractId, version, resolve, {
|
|
29
|
+
providerPackageId: registration.providerPackageId,
|
|
30
|
+
});
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
const key = registration.contractId && registration.version != null
|
package/dist/data/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./group-secret";
|
|
|
13
13
|
export * from "./group-share";
|
|
14
14
|
export * from "./groups";
|
|
15
15
|
export * from "./messages";
|
|
16
|
+
export * from "./package-table-identity";
|
|
16
17
|
export * from "./package-version-resolver";
|
|
17
18
|
export * from "./package-versions";
|
|
18
19
|
export * from "./packages";
|
package/dist/data/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __exportStar(require("./group-secret"), exports);
|
|
|
29
29
|
__exportStar(require("./group-share"), exports);
|
|
30
30
|
__exportStar(require("./groups"), exports);
|
|
31
31
|
__exportStar(require("./messages"), exports);
|
|
32
|
+
__exportStar(require("./package-table-identity"), exports);
|
|
32
33
|
__exportStar(require("./package-version-resolver"), exports);
|
|
33
34
|
__exportStar(require("./package-versions"), exports);
|
|
34
35
|
__exportStar(require("./packages"), exports);
|
package/dist/data/orm/types.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type IField } from "../../types/field-type";
|
|
3
|
+
/** Pure-data schema and storage metadata for a Peers table. */
|
|
3
4
|
export interface ITableMetaData {
|
|
4
5
|
tableId?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Trusted owning package for package-scoped physical naming.
|
|
8
|
+
*
|
|
9
|
+
* When present, physical naming uses `name_packageId` while `tableId`
|
|
10
|
+
* remains the independent schema-definition identity.
|
|
11
|
+
*/
|
|
12
|
+
packageId?: string;
|
|
5
13
|
name: string;
|
|
6
14
|
description: string;
|
|
7
15
|
primaryKeyName: string;
|
|
@@ -17,5 +25,16 @@ export interface ITableMetaData {
|
|
|
17
25
|
localOnly?: boolean;
|
|
18
26
|
versionNumber?: number;
|
|
19
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert field metadata to the runtime Zod schema used by table validation.
|
|
30
|
+
*
|
|
31
|
+
* @param fields Serializable field definitions.
|
|
32
|
+
* @param codeSchema Optional author schema whose refinements are intersected.
|
|
33
|
+
*/
|
|
20
34
|
export declare function fieldsToSchema(fields: IField[], codeSchema?: z.AnyZodObject): z.AnyZodObject;
|
|
35
|
+
/**
|
|
36
|
+
* Convert a Zod object schema to serializable field metadata.
|
|
37
|
+
*
|
|
38
|
+
* @param schema Runtime record schema.
|
|
39
|
+
*/
|
|
21
40
|
export declare function schemaToFields(schema: z.AnyZodObject): IField[];
|
package/dist/data/orm/types.js
CHANGED
|
@@ -5,6 +5,12 @@ exports.schemaToFields = schemaToFields;
|
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const field_type_1 = require("../../types/field-type");
|
|
7
7
|
const utils_1 = require("../../utils");
|
|
8
|
+
/**
|
|
9
|
+
* Convert field metadata to the runtime Zod schema used by table validation.
|
|
10
|
+
*
|
|
11
|
+
* @param fields Serializable field definitions.
|
|
12
|
+
* @param codeSchema Optional author schema whose refinements are intersected.
|
|
13
|
+
*/
|
|
8
14
|
function fieldsToSchema(fields, codeSchema) {
|
|
9
15
|
let argsSchema = zod_1.z.object({});
|
|
10
16
|
for (const argSchema of fields) {
|
|
@@ -52,6 +58,11 @@ function fieldsToSchema(fields, codeSchema) {
|
|
|
52
58
|
}
|
|
53
59
|
return argsSchema;
|
|
54
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Convert a Zod object schema to serializable field metadata.
|
|
63
|
+
*
|
|
64
|
+
* @param schema Runtime record schema.
|
|
65
|
+
*/
|
|
55
66
|
function schemaToFields(schema) {
|
|
56
67
|
const fields = [];
|
|
57
68
|
for (const name of Object.keys(schema.shape)) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a guest-supplied isolated-table or field identifier before it can
|
|
3
|
+
* reach ORM-generated SQL.
|
|
4
|
+
*
|
|
5
|
+
* This intentionally accepts only simple SQLite identifiers. The package
|
|
6
|
+
* manifest validator and host gateway can share this helper so malformed
|
|
7
|
+
* names are rejected even when a declaration bypasses artifact loading.
|
|
8
|
+
*
|
|
9
|
+
* @param identifier Guest-supplied logical table or field name.
|
|
10
|
+
* @param label Description used in validation errors.
|
|
11
|
+
* @returns The unchanged identifier.
|
|
12
|
+
* @throws When the identifier is empty or contains SQL syntax.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateIsolatedSqlIdentifier(identifier: string, label?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Validate a package-local table name.
|
|
17
|
+
*
|
|
18
|
+
* @param logicalName Guest-visible table name.
|
|
19
|
+
* @returns The unchanged logical name.
|
|
20
|
+
* @throws When the name is not a SQL-safe isolated identifier.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validatePackageTableName(logicalName: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Derive the physical SQLite name for a package-owned table.
|
|
25
|
+
*
|
|
26
|
+
* Package ID is appended so authors only need unique logical names inside
|
|
27
|
+
* their own package.
|
|
28
|
+
*
|
|
29
|
+
* @param packageId Trusted owning package ID.
|
|
30
|
+
* @param logicalName Package-local table name.
|
|
31
|
+
* @returns The package-scoped physical table name.
|
|
32
|
+
* @throws When package or table identity is invalid.
|
|
33
|
+
*/
|
|
34
|
+
export declare function packageScopedTableName(packageId: string, logicalName: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Derive the stable host-only ID used to key `TableDefinitions`.
|
|
37
|
+
*
|
|
38
|
+
* The ID is intentionally separate from the physical SQLite name so schema
|
|
39
|
+
* tracking keeps a valid peer ID without producing a triple-suffixed table.
|
|
40
|
+
*
|
|
41
|
+
* @param packageId Trusted owning package ID.
|
|
42
|
+
* @param logicalName Package-local table name.
|
|
43
|
+
* @returns A deterministic peer ID for the package-owned table definition.
|
|
44
|
+
* @throws When package or table identity is invalid.
|
|
45
|
+
*/
|
|
46
|
+
export declare function packageScopedTableId(packageId: string, logicalName: string): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateIsolatedSqlIdentifier = validateIsolatedSqlIdentifier;
|
|
4
|
+
exports.validatePackageTableName = validatePackageTableName;
|
|
5
|
+
exports.packageScopedTableName = packageScopedTableName;
|
|
6
|
+
exports.packageScopedTableId = packageScopedTableId;
|
|
7
|
+
const utils_1 = require("../utils");
|
|
8
|
+
const ISOLATED_SQL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9
|
+
/**
|
|
10
|
+
* Validate a guest-supplied isolated-table or field identifier before it can
|
|
11
|
+
* reach ORM-generated SQL.
|
|
12
|
+
*
|
|
13
|
+
* This intentionally accepts only simple SQLite identifiers. The package
|
|
14
|
+
* manifest validator and host gateway can share this helper so malformed
|
|
15
|
+
* names are rejected even when a declaration bypasses artifact loading.
|
|
16
|
+
*
|
|
17
|
+
* @param identifier Guest-supplied logical table or field name.
|
|
18
|
+
* @param label Description used in validation errors.
|
|
19
|
+
* @returns The unchanged identifier.
|
|
20
|
+
* @throws When the identifier is empty or contains SQL syntax.
|
|
21
|
+
*/
|
|
22
|
+
function validateIsolatedSqlIdentifier(identifier, label = "Isolated SQL identifier") {
|
|
23
|
+
if (typeof identifier !== "string" || identifier.trim() === "") {
|
|
24
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
25
|
+
}
|
|
26
|
+
if (!ISOLATED_SQL_IDENTIFIER.test(identifier)) {
|
|
27
|
+
throw new Error(`${label} must start with a letter or underscore and contain only letters, numbers, and underscores`);
|
|
28
|
+
}
|
|
29
|
+
return identifier;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validate a package-local table name.
|
|
33
|
+
*
|
|
34
|
+
* @param logicalName Guest-visible table name.
|
|
35
|
+
* @returns The unchanged logical name.
|
|
36
|
+
* @throws When the name is not a SQL-safe isolated identifier.
|
|
37
|
+
*/
|
|
38
|
+
function validatePackageTableName(logicalName) {
|
|
39
|
+
return validateIsolatedSqlIdentifier(logicalName, "Package table name");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Derive the physical SQLite name for a package-owned table.
|
|
43
|
+
*
|
|
44
|
+
* Package ID is appended so authors only need unique logical names inside
|
|
45
|
+
* their own package.
|
|
46
|
+
*
|
|
47
|
+
* @param packageId Trusted owning package ID.
|
|
48
|
+
* @param logicalName Package-local table name.
|
|
49
|
+
* @returns The package-scoped physical table name.
|
|
50
|
+
* @throws When package or table identity is invalid.
|
|
51
|
+
*/
|
|
52
|
+
function packageScopedTableName(packageId, logicalName) {
|
|
53
|
+
if (!(0, utils_1.isid)(packageId)) {
|
|
54
|
+
throw new Error(`Invalid package ID for table: ${packageId}`);
|
|
55
|
+
}
|
|
56
|
+
return `${validatePackageTableName(logicalName)}_${packageId}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Derive the stable host-only ID used to key `TableDefinitions`.
|
|
60
|
+
*
|
|
61
|
+
* The ID is intentionally separate from the physical SQLite name so schema
|
|
62
|
+
* tracking keeps a valid peer ID without producing a triple-suffixed table.
|
|
63
|
+
*
|
|
64
|
+
* @param packageId Trusted owning package ID.
|
|
65
|
+
* @param logicalName Package-local table name.
|
|
66
|
+
* @returns A deterministic peer ID for the package-owned table definition.
|
|
67
|
+
* @throws When package or table identity is invalid.
|
|
68
|
+
*/
|
|
69
|
+
function packageScopedTableId(packageId, logicalName) {
|
|
70
|
+
return (0, utils_1.deterministicTableId)(packageScopedTableName(packageId, logicalName));
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("../utils");
|
|
4
|
+
const package_table_identity_1 = require("./package-table-identity");
|
|
5
|
+
describe("package-scoped table identity", () => {
|
|
6
|
+
test("appends trusted package identity to the logical table name", () => {
|
|
7
|
+
const packageId = (0, utils_1.newid)();
|
|
8
|
+
const tableId = (0, package_table_identity_1.packageScopedTableId)(packageId, "Settings");
|
|
9
|
+
const metaData = {
|
|
10
|
+
packageId,
|
|
11
|
+
tableId,
|
|
12
|
+
name: "Settings",
|
|
13
|
+
description: "Package settings",
|
|
14
|
+
primaryKeyName: "settingId",
|
|
15
|
+
fields: [],
|
|
16
|
+
};
|
|
17
|
+
expect((0, package_table_identity_1.packageScopedTableName)(packageId, "Settings")).toBe(`Settings_${packageId}`);
|
|
18
|
+
expect((0, utils_1.getFullTableName)(metaData)).toBe(`Settings_${packageId}`);
|
|
19
|
+
expect((0, utils_1.getFullTableName)(metaData)).not.toContain(tableId);
|
|
20
|
+
expect((0, utils_1.isid)(tableId)).toBe(true);
|
|
21
|
+
expect((0, utils_1.isDeterministicTableId)(tableId)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
test("keeps existing table naming unchanged", () => {
|
|
24
|
+
const tableId = (0, utils_1.newid)();
|
|
25
|
+
expect((0, utils_1.getFullTableName)({
|
|
26
|
+
tableId,
|
|
27
|
+
name: "Settings",
|
|
28
|
+
description: "Existing table",
|
|
29
|
+
primaryKeyName: "settingId",
|
|
30
|
+
fields: [],
|
|
31
|
+
})).toBe(`Settings_${tableId}`);
|
|
32
|
+
});
|
|
33
|
+
test("uses package and logical name for stable internal IDs", () => {
|
|
34
|
+
const packageId = (0, utils_1.newid)();
|
|
35
|
+
expect((0, package_table_identity_1.packageScopedTableId)(packageId, "One")).toBe((0, package_table_identity_1.packageScopedTableId)(packageId, "One"));
|
|
36
|
+
expect((0, package_table_identity_1.packageScopedTableId)(packageId, "One")).not.toBe((0, package_table_identity_1.packageScopedTableId)(packageId, "Two"));
|
|
37
|
+
expect((0, package_table_identity_1.packageScopedTableId)(packageId, "One")).not.toBe((0, package_table_identity_1.packageScopedTableId)((0, utils_1.newid)(), "One"));
|
|
38
|
+
});
|
|
39
|
+
test("rejects invalid package IDs and empty logical names", () => {
|
|
40
|
+
expect(() => (0, package_table_identity_1.packageScopedTableName)("not-an-id", "Settings")).toThrow(/package ID/);
|
|
41
|
+
expect(() => (0, package_table_identity_1.packageScopedTableName)((0, utils_1.newid)(), " ")).toThrow(/non-empty/);
|
|
42
|
+
expect(() => (0, package_table_identity_1.packageScopedTableName)((0, utils_1.newid)(), 'State"; DROP TABLE Users; --')).toThrow(/only letters, numbers, and underscores/);
|
|
43
|
+
expect((0, package_table_identity_1.validateIsolatedSqlIdentifier)("state_value", "field")).toBe("state_value");
|
|
44
|
+
expect(() => (0, package_table_identity_1.validateIsolatedSqlIdentifier)("state.value", "field")).toThrow(/field/);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -18,6 +18,8 @@ import { type PersistentVar } from "./persistent-vars";
|
|
|
18
18
|
export interface IDevicePackagePrefs {
|
|
19
19
|
/** Which PV this device currently runs */
|
|
20
20
|
activePackageVersionId?: string;
|
|
21
|
+
/** Newer selected PV temporarily bypassed because this host could not run it. */
|
|
22
|
+
compatibilityFallbackForPackageVersionId?: string;
|
|
21
23
|
/** If true, never auto-update this package */
|
|
22
24
|
pinned?: boolean;
|
|
23
25
|
/** Auto-update range: patch | minor | latest */
|
|
@@ -98,3 +100,14 @@ export declare function resolveDevicePackageVersion(pkg: IPackage, dataContext:
|
|
|
98
100
|
* to advance the group-level active version on upgrade.
|
|
99
101
|
*/
|
|
100
102
|
export declare function hasDeviceFollowOverride(prefs: IDevicePackagePrefs | undefined): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Read the stored local checkout path for a package, if one is set.
|
|
105
|
+
*
|
|
106
|
+
* Used so isolated local-dev packages can load `dist/package.bundle.js` from
|
|
107
|
+
* disk even when startup resolution does not receive an explicit `localPath`.
|
|
108
|
+
*
|
|
109
|
+
* @param packageId - Package whose `packageLocalPath_*` device var to read.
|
|
110
|
+
* @param dataContext - Data context that owns the device var.
|
|
111
|
+
* @returns Trimmed path, or `undefined` when unset or empty.
|
|
112
|
+
*/
|
|
113
|
+
export declare function readStoredPackageLocalPath(packageId: string, dataContext: DataContext): Promise<string | undefined>;
|