@lunora/server 1.0.0-alpha.3 → 1.0.0-alpha.31
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/LICENSE.md +6 -0
- package/README.md +50 -0
- package/dist/data-model.d.mts +231 -156
- package/dist/data-model.d.ts +231 -156
- package/dist/index.d.mts +1191 -811
- package/dist/index.d.ts +1191 -811
- package/dist/index.mjs +20 -15
- package/dist/packem_shared/{LunoraEnvError-DjFkpkSP.mjs → LunoraEnvError-BGmd1Qs0.mjs} +4 -4
- package/dist/packem_shared/LunoraError-WbxmrpxR.mjs +9 -0
- package/dist/packem_shared/{PRESENCE_DEFAULT_TTL_MS-UQuUI5sV.mjs → PRESENCE_DEFAULT_TTL_MS-Cr0i4mTv.mjs} +5 -5
- package/dist/packem_shared/{bindOrm-Ce57S3N9.mjs → bindOrm-CaY7Wq9Z.mjs} +42 -23
- package/dist/packem_shared/buildRlsReadRegistry-2uk_GfiH.mjs +107 -0
- package/dist/packem_shared/{composePluginMiddleware-Ck5_TUO8.mjs → composePluginMiddleware-z62dttBo.mjs} +11 -8
- package/dist/packem_shared/{createPolicyDsl-De67zPDS.mjs → createPolicyDsl-By3QB4he.mjs} +4 -1
- package/dist/packem_shared/createSecrets-DwaR2rNG.mjs +58 -0
- package/dist/packem_shared/{defineAggregateIndex-B20MIOmj.mjs → defineAggregateIndex-cdo0g-un.mjs} +108 -8
- package/dist/packem_shared/defineIdentity-DiX4zM9x.mjs +35 -0
- package/dist/packem_shared/{defineMigration-CAJLr6fx.mjs → defineMigration-Hx01yIht.mjs} +3 -1
- package/dist/packem_shared/defineMutator-EIXAWhs9.mjs +11 -0
- package/dist/packem_shared/defineShape-C5scNOrf.mjs +18 -0
- package/dist/packem_shared/{defineStorageRule-qu0mpilX.mjs → defineStorageRule-B5nL4Z1P.mjs} +4 -1
- package/dist/packem_shared/functions-Di9FUNkf.mjs +5 -0
- package/dist/packem_shared/{httpAction-B7FYUEgr.mjs → httpAction-DCXoYPIk.mjs} +42 -27
- package/dist/packem_shared/{initLunora-CATvPsVt.mjs → initLunora-D0Wuki7S.mjs} +36 -7
- package/dist/packem_shared/{mask-eCUYOwhd.mjs → mask-BepaW7YN.mjs} +132 -9
- package/dist/packem_shared/policy-tag-DvpVH2tv.mjs +13 -0
- package/dist/packem_shared/{protectPublic-BjFkQ_Or.mjs → protectPublic-BlcGpiRc.mjs} +1 -1
- package/dist/packem_shared/{rls-Bi9HiyDC.mjs → rls-BaDQf7MG.mjs} +34 -6
- package/dist/packem_shared/{run-middleware-CYQOuoV6.mjs → run-middleware-I6EiQfxL.mjs} +3 -1
- package/dist/packem_shared/{storageRules-4a30FSpI.mjs → storageRules-6QxzDOcx.mjs} +1 -1
- package/dist/packem_shared/types.d-C4CMJK8x.d.mts +141 -0
- package/dist/packem_shared/types.d-DdYF8E18.d.ts +141 -0
- package/dist/rls/testing.d.mts +31 -31
- package/dist/rls/testing.d.ts +31 -31
- package/dist/rls/testing.mjs +1 -1
- package/dist/types.d.mts +957 -433
- package/dist/types.d.ts +957 -433
- package/package.json +6 -5
- package/dist/packem_shared/LunoraError-DhggBJZF.mjs +0 -51
- package/dist/packem_shared/types.d-BDY0FYHK.d.ts +0 -135
- package/dist/packem_shared/types.d-DmvyEMD6.d.mts +0 -135
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
export { default as asBucketStorage } from './packem_shared/asBucketStorage-Cnxd9y2q.mjs';
|
|
2
|
-
export { initLunora } from './packem_shared/initLunora-
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
2
|
+
export { initLunora } from './packem_shared/initLunora-D0Wuki7S.mjs';
|
|
3
|
+
export { createSecrets } from './packem_shared/createSecrets-DwaR2rNG.mjs';
|
|
4
|
+
export { LunoraEnvError, defineEnv, redactSecrets } from './packem_shared/LunoraEnvError-BGmd1Qs0.mjs';
|
|
5
|
+
export { LunoraError } from './packem_shared/LunoraError-WbxmrpxR.mjs';
|
|
6
|
+
export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-CaY7Wq9Z.mjs';
|
|
7
|
+
export { httpAction, httpRoute, httpRouter, isSafeHeaderValue, serveStorageObject } from './packem_shared/httpAction-DCXoYPIk.mjs';
|
|
8
|
+
export { defineIdentity } from './packem_shared/defineIdentity-DiX4zM9x.mjs';
|
|
7
9
|
export { onConnect, onDisconnect } from './packem_shared/onConnect-CIPXKPyw.mjs';
|
|
8
|
-
export { defineMigration } from './packem_shared/defineMigration-
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
10
|
+
export { defineMigration } from './packem_shared/defineMigration-Hx01yIht.mjs';
|
|
11
|
+
export { defineMutator } from './packem_shared/defineMutator-EIXAWhs9.mjs';
|
|
12
|
+
export { composePluginMiddleware, defineComponent, definePlugin, defineSchemaExtension, installPlugins, mergeSchemaExtension } from './packem_shared/composePluginMiddleware-z62dttBo.mjs';
|
|
13
|
+
export { PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, definePresence, presenceExtension } from './packem_shared/PRESENCE_DEFAULT_TTL_MS-Cr0i4mTv.mjs';
|
|
14
|
+
export { protectPublic } from './packem_shared/protectPublic-BlcGpiRc.mjs';
|
|
15
|
+
export { defineAggregateIndex, defineRankIndex, defineSchema, defineTable, defineVectorIndex } from './packem_shared/defineAggregateIndex-cdo0g-un.mjs';
|
|
16
|
+
export { defineShape } from './packem_shared/defineShape-C5scNOrf.mjs';
|
|
13
17
|
export { anyApi } from './types.mjs';
|
|
14
18
|
export { cronJobs } from '@lunora/scheduler';
|
|
15
19
|
export { ValidationError, v } from '@lunora/values';
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
20
|
+
export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-2uk_GfiH.mjs';
|
|
21
|
+
export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-By3QB4he.mjs';
|
|
22
|
+
export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-B5nL4Z1P.mjs';
|
|
23
|
+
export { mask } from './packem_shared/mask-BepaW7YN.mjs';
|
|
24
|
+
export { rls } from './packem_shared/rls-BaDQf7MG.mjs';
|
|
25
|
+
export { storageRules } from './packem_shared/storageRules-6QxzDOcx.mjs';
|
|
21
26
|
|
|
22
27
|
const VERSION = "0.0.0";
|
|
23
28
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
1
2
|
import { optionalInner } from '@lunora/values';
|
|
2
3
|
|
|
3
4
|
const SECRET_VALUE_PREFIXES = [
|
|
@@ -53,13 +54,12 @@ const redactValueForKey = (message, key, raw) => {
|
|
|
53
54
|
}
|
|
54
55
|
return masked;
|
|
55
56
|
};
|
|
56
|
-
class LunoraEnvError extends
|
|
57
|
-
name = "LunoraEnvError";
|
|
57
|
+
class LunoraEnvError extends LunoraError$1 {
|
|
58
58
|
failures;
|
|
59
59
|
constructor(failures) {
|
|
60
60
|
const summary = failures.map((failure) => ` - ${failure.key}: ${failure.message}`).join("\n");
|
|
61
|
-
super(`Invalid environment (${String(failures.length)} key(s)):
|
|
62
|
-
${summary}
|
|
61
|
+
super("ENV_INVALID", `Invalid environment (${String(failures.length)} key(s)):
|
|
62
|
+
${summary}`, { name: "LunoraEnvError" });
|
|
63
63
|
this.failures = failures;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { v } from '@lunora/values';
|
|
2
|
-
import { initLunora } from './initLunora-
|
|
3
|
-
import { LunoraError } from './LunoraError-
|
|
2
|
+
import { initLunora } from './initLunora-D0Wuki7S.mjs';
|
|
3
|
+
import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
|
|
4
4
|
import { onDisconnect } from './onConnect-CIPXKPyw.mjs';
|
|
5
|
-
import { defineSchemaExtension, defineComponent } from './composePluginMiddleware-
|
|
6
|
-
import { defineTable } from './defineAggregateIndex-
|
|
5
|
+
import { defineSchemaExtension, defineComponent } from './composePluginMiddleware-z62dttBo.mjs';
|
|
6
|
+
import { defineTable } from './defineAggregateIndex-cdo0g-un.mjs';
|
|
7
7
|
|
|
8
8
|
const DEFAULT_TTL_MS = 3e4;
|
|
9
9
|
const MAX_DATA_BYTES = 4096;
|
|
@@ -32,7 +32,7 @@ const definePresence = (options = {}) => {
|
|
|
32
32
|
}).mutation(async ({ args, ctx: context }) => {
|
|
33
33
|
const lastSeen = Date.now();
|
|
34
34
|
const userId = context.auth.userId ?? void 0;
|
|
35
|
-
if (args.data !== void 0 && JSON.stringify(args.data).length > MAX_DATA_BYTES) {
|
|
35
|
+
if (args.data !== void 0 && new TextEncoder().encode(JSON.stringify(args.data)).length > MAX_DATA_BYTES) {
|
|
36
36
|
throw new LunoraError("BAD_REQUEST", `presence data exceeds the ${String(MAX_DATA_BYTES)}-byte limit`);
|
|
37
37
|
}
|
|
38
38
|
const existing = await context.db.query(PRESENCE_TABLE).withIndex("byRoomSession", (q) => q.eq("roomId", args.roomId).eq("sessionId", args.sessionId)).first();
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const isUniqueConflict = (error) => typeof error === "object" && error !== null && error.code === "CONFLICT" && error.kind === "unique";
|
|
2
4
|
const buildUpsertWhere = (tableName, target, create) => {
|
|
3
5
|
const fields = typeof target === "string" ? [target] : target;
|
|
4
6
|
if (fields.length === 0) {
|
|
5
|
-
throw new
|
|
7
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: "target" must name at least one field`);
|
|
6
8
|
}
|
|
7
9
|
const where = {};
|
|
8
10
|
for (const field of fields) {
|
|
9
11
|
if (!(field in create)) {
|
|
10
|
-
throw new
|
|
12
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: target field "${field}" is missing from the create document`);
|
|
11
13
|
}
|
|
12
14
|
where[field] = create[field];
|
|
13
15
|
}
|
|
@@ -42,15 +44,24 @@ const bindTableFacade = (writer, tableName) => {
|
|
|
42
44
|
count: (where) => writer.count(tableName, where),
|
|
43
45
|
delete: (id) => writer.delete(id, tableName),
|
|
44
46
|
// `deleteMany`/`patchMany` forward the bound `tableName` as `expectedTable`
|
|
45
|
-
// (threaded through the writer + the RLS middleware's
|
|
46
|
-
// batched id is scoped to this table — the same IDOR
|
|
47
|
-
// `delete`/`patch` apply.
|
|
48
|
-
// the writer's `{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// for id-based calls (threaded through the writer + the RLS middleware's
|
|
48
|
+
// per-id gate) so every batched id is scoped to this table — the same IDOR
|
|
49
|
+
// guard the single-row `delete`/`patch` apply. The where-based form routes
|
|
50
|
+
// through the structural writer's `deleteMany(tableName, { where })`.
|
|
51
|
+
// `patchMany` maps the facade's `values` payload to the writer's
|
|
52
|
+
// `{ id, patch }` shape.
|
|
53
|
+
deleteMany: (first, options) => {
|
|
54
|
+
if (Array.isArray(first)) {
|
|
55
|
+
if (writer.deleteMany === void 0) {
|
|
56
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.deleteMany is unavailable: this writer has no batch delete`);
|
|
57
|
+
}
|
|
58
|
+
return writer.deleteMany(first, options, tableName);
|
|
59
|
+
}
|
|
60
|
+
if (writer.deleteWhere === void 0) {
|
|
61
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.deleteMany({ where }) is unavailable: this writer has no where-based delete`);
|
|
52
62
|
}
|
|
53
|
-
|
|
63
|
+
const whereArgs = first;
|
|
64
|
+
return writer.deleteWhere(tableName, whereArgs.where, { limit: whereArgs.limit });
|
|
54
65
|
},
|
|
55
66
|
// `exists` reuses `findFirst` (RLS-filtered, indexed when a `.withIndex`-able
|
|
56
67
|
// `where` is supplied) and only asks whether a row came back — no count scan.
|
|
@@ -65,29 +76,36 @@ const bindTableFacade = (writer, tableName) => {
|
|
|
65
76
|
insert,
|
|
66
77
|
insertMany: (documents, options) => {
|
|
67
78
|
if (writer.insertMany === void 0) {
|
|
68
|
-
throw new
|
|
79
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.insertMany is unavailable: this writer has no batch insert`);
|
|
69
80
|
}
|
|
70
81
|
return writer.insertMany(tableName, documents, options);
|
|
71
82
|
},
|
|
72
83
|
patch: (id, patch) => writer.patch(id, patch, tableName),
|
|
73
|
-
patchMany: (
|
|
74
|
-
if (
|
|
75
|
-
|
|
84
|
+
patchMany: (first, options) => {
|
|
85
|
+
if (Array.isArray(first)) {
|
|
86
|
+
if (writer.patchMany === void 0) {
|
|
87
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.patchMany is unavailable: this writer has no batch patch`);
|
|
88
|
+
}
|
|
89
|
+
return writer.patchMany(
|
|
90
|
+
first.map((entry) => {
|
|
91
|
+
return { id: entry.id, patch: entry.values };
|
|
92
|
+
}),
|
|
93
|
+
options,
|
|
94
|
+
tableName
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (writer.patchWhere === void 0) {
|
|
98
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.patchMany({ where, values }) is unavailable: this writer has no where-based patch`);
|
|
76
99
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return { id: entry.id, patch: entry.values };
|
|
80
|
-
}),
|
|
81
|
-
options,
|
|
82
|
-
tableName
|
|
83
|
-
);
|
|
100
|
+
const whereArgs = first;
|
|
101
|
+
return writer.patchWhere(tableName, { patch: whereArgs.values, where: whereArgs.where }, { limit: whereArgs.limit });
|
|
84
102
|
},
|
|
85
103
|
rank: (indexName, options) => writer.rank(tableName, indexName, options),
|
|
86
104
|
rankPage: (indexName, options) => writer.rankPage(tableName, indexName, options),
|
|
87
105
|
replace: (id, document) => writer.replace(id, document, tableName),
|
|
88
106
|
restore: async (id) => {
|
|
89
107
|
if (!writer.restore) {
|
|
90
|
-
throw new
|
|
108
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.restore is unavailable: this writer has no restore (is the table .softDelete()?)`);
|
|
91
109
|
}
|
|
92
110
|
await writer.restore(id, tableName);
|
|
93
111
|
},
|
|
@@ -99,6 +117,7 @@ const bindTableFacade = (writer, tableName) => {
|
|
|
99
117
|
}
|
|
100
118
|
return results;
|
|
101
119
|
},
|
|
120
|
+
withGeoIndex: (indexName, build) => writer.query(tableName).withGeoIndex(indexName, build),
|
|
102
121
|
withSearchIndex: (indexName, search) => writer.query(tableName).withSearchIndex(indexName, search)
|
|
103
122
|
};
|
|
104
123
|
};
|
|
@@ -106,7 +125,7 @@ const bindOrm = (facade) => {
|
|
|
106
125
|
const resolve = (table) => {
|
|
107
126
|
const bound = facade[table];
|
|
108
127
|
if (!bound) {
|
|
109
|
-
throw new
|
|
128
|
+
throw new LunoraError$1("INTERNAL", `unknown table: ${table}`);
|
|
110
129
|
}
|
|
111
130
|
return bound;
|
|
112
131
|
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-BaDQf7MG.mjs';
|
|
2
|
+
import { r as readRlsTag } from './policy-tag-DvpVH2tv.mjs';
|
|
3
|
+
|
|
4
|
+
const FALSE_PREDICATE = { OR: [] };
|
|
5
|
+
const readEntryTags = (entry) => {
|
|
6
|
+
const hoisted = entry?.rls?.tags;
|
|
7
|
+
if (hoisted) {
|
|
8
|
+
return hoisted;
|
|
9
|
+
}
|
|
10
|
+
const tag = readRlsTag(entry);
|
|
11
|
+
return tag ? [tag] : [];
|
|
12
|
+
};
|
|
13
|
+
const addTagToRegistry = (byTable, tag) => {
|
|
14
|
+
const rolePermissions = indexRolePermissions(tag.roles);
|
|
15
|
+
const seenWhenByTable = /* @__PURE__ */ new Map();
|
|
16
|
+
const policiesByTable = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const policy of tag.policies) {
|
|
18
|
+
if (policy.on !== "read") {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const seen = seenWhenByTable.get(policy.table) ?? /* @__PURE__ */ new Set();
|
|
22
|
+
if (seen.has(policy.when)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
seen.add(policy.when);
|
|
26
|
+
seenWhenByTable.set(policy.table, seen);
|
|
27
|
+
const list = policiesByTable.get(policy.table) ?? [];
|
|
28
|
+
list.push(policy);
|
|
29
|
+
policiesByTable.set(policy.table, list);
|
|
30
|
+
}
|
|
31
|
+
for (const [table, policies] of policiesByTable) {
|
|
32
|
+
const groups = byTable.get(table) ?? [];
|
|
33
|
+
groups.push({ policies, rolePermissions });
|
|
34
|
+
byTable.set(table, groups);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const isFalsePredicate = (where) => {
|
|
38
|
+
const or = where.OR;
|
|
39
|
+
return Array.isArray(or) && or.length === 0 && Object.keys(where).length === 1;
|
|
40
|
+
};
|
|
41
|
+
const andMerge = (injected, caller) => {
|
|
42
|
+
if (!injected || Object.keys(injected).length === 0) {
|
|
43
|
+
return caller;
|
|
44
|
+
}
|
|
45
|
+
if (isFalsePredicate(injected)) {
|
|
46
|
+
return injected;
|
|
47
|
+
}
|
|
48
|
+
if (Object.keys(caller).length === 0) {
|
|
49
|
+
return injected;
|
|
50
|
+
}
|
|
51
|
+
return { AND: [injected, caller] };
|
|
52
|
+
};
|
|
53
|
+
const evaluateGroupBaseWhere = (group, request) => {
|
|
54
|
+
const granted = /* @__PURE__ */ new Set();
|
|
55
|
+
for (const roleName of request.roles) {
|
|
56
|
+
for (const name of group.rolePermissions.get(roleName) ?? []) {
|
|
57
|
+
granted.add(name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return computeReadBaseWhere(group.policies, {
|
|
61
|
+
auth: {
|
|
62
|
+
can: (permission) => granted.has(permissionName(permission)),
|
|
63
|
+
identity: request.identity,
|
|
64
|
+
roles: request.roles,
|
|
65
|
+
userId: request.userId
|
|
66
|
+
},
|
|
67
|
+
ctx: request.ctx
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const resolveReadBaseWhere = (registry, request) => {
|
|
71
|
+
const groups = registry.byTable.get(request.table);
|
|
72
|
+
if (!groups || groups.length === 0) {
|
|
73
|
+
return request.rlsRequired && !request.tablePublic ? FALSE_PREDICATE : void 0;
|
|
74
|
+
}
|
|
75
|
+
const predicates = [];
|
|
76
|
+
for (const group of groups) {
|
|
77
|
+
const base = evaluateGroupBaseWhere(group, request);
|
|
78
|
+
if (base === void 0) {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
if (isFalsePredicate(base)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
predicates.push(base);
|
|
85
|
+
}
|
|
86
|
+
if (predicates.length === 0) {
|
|
87
|
+
return FALSE_PREDICATE;
|
|
88
|
+
}
|
|
89
|
+
return predicates.length === 1 ? predicates[0] : { OR: predicates };
|
|
90
|
+
};
|
|
91
|
+
const buildRlsReadRegistry = (functions) => {
|
|
92
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
93
|
+
const seenTags = /* @__PURE__ */ new Set();
|
|
94
|
+
for (const entry of functions) {
|
|
95
|
+
for (const tag of readEntryTags(entry)) {
|
|
96
|
+
if (seenTags.has(tag)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
seenTags.add(tag);
|
|
100
|
+
addTagToRegistry(byTable, tag);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { byTable };
|
|
104
|
+
};
|
|
105
|
+
const composeShapeReadWhere = (registry, request) => andMerge(resolveReadBaseWhere(registry, request), request.shapeWhere);
|
|
106
|
+
|
|
107
|
+
export { buildRlsReadRegistry, composeShapeReadWhere };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
+
import { r as runMiddlewareChain } from './run-middleware-I6EiQfxL.mjs';
|
|
2
3
|
|
|
3
4
|
const prefixTableName = (key, bareName) => `${key}_${bareName}`;
|
|
4
5
|
const rewriteReference = (target, key, bareNames) => bareNames.has(target) ? prefixTableName(key, target) : target;
|
|
@@ -19,7 +20,7 @@ const rewriteTableReferences = (table, key, bareNames, ownBareName) => {
|
|
|
19
20
|
};
|
|
20
21
|
const defineSchemaExtension = (key, options) => {
|
|
21
22
|
if (!key) {
|
|
22
|
-
throw new
|
|
23
|
+
throw new LunoraError$1("INTERNAL", "defineSchemaExtension: `key` is required and must be a non-empty string");
|
|
23
24
|
}
|
|
24
25
|
return {
|
|
25
26
|
key,
|
|
@@ -27,19 +28,19 @@ const defineSchemaExtension = (key, options) => {
|
|
|
27
28
|
...options.vectorIndexes ? { vectorIndexes: options.vectorIndexes } : {}
|
|
28
29
|
};
|
|
29
30
|
};
|
|
30
|
-
const definePlugin = (key, options) => {
|
|
31
|
+
const definePlugin = ((key, options) => {
|
|
31
32
|
if (!key) {
|
|
32
|
-
throw new
|
|
33
|
+
throw new LunoraError$1("INTERNAL", "definePlugin: `key` is required and must be a non-empty string");
|
|
33
34
|
}
|
|
34
35
|
if (options.extension && options.extension.key !== key) {
|
|
35
|
-
throw new
|
|
36
|
+
throw new LunoraError$1("INTERNAL", `definePlugin("${key}"): extension key "${options.extension.key}" does not match plugin key`);
|
|
36
37
|
}
|
|
37
38
|
return {
|
|
38
39
|
key,
|
|
39
40
|
...options.extension ? { extension: options.extension } : {},
|
|
40
41
|
...options.middleware ? { middleware: options.middleware } : {}
|
|
41
42
|
};
|
|
42
|
-
};
|
|
43
|
+
});
|
|
43
44
|
const defineComponent = (key, options) => {
|
|
44
45
|
const plugin = definePlugin(key, {
|
|
45
46
|
...options.extension ? { extension: options.extension } : {},
|
|
@@ -57,7 +58,8 @@ const mergeSchemaExtension = (base, extension) => {
|
|
|
57
58
|
for (const [bareName, table] of Object.entries(extension.tables)) {
|
|
58
59
|
const prefixed = prefixTableName(key, bareName);
|
|
59
60
|
if (Object.hasOwn(merged, prefixed)) {
|
|
60
|
-
throw new
|
|
61
|
+
throw new LunoraError$1(
|
|
62
|
+
"INTERNAL",
|
|
61
63
|
`defineSchema(...).extend("${key}"): table "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
|
|
62
64
|
);
|
|
63
65
|
}
|
|
@@ -68,7 +70,8 @@ const mergeSchemaExtension = (base, extension) => {
|
|
|
68
70
|
for (const [bareIndexName, index] of Object.entries(extension.vectorIndexes)) {
|
|
69
71
|
const prefixed = prefixTableName(key, bareIndexName);
|
|
70
72
|
if (Object.hasOwn(mergedVectorIndexes, prefixed)) {
|
|
71
|
-
throw new
|
|
73
|
+
throw new LunoraError$1(
|
|
74
|
+
"INTERNAL",
|
|
72
75
|
`defineSchema(...).extend("${key}"): vector index "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
|
|
73
76
|
);
|
|
74
77
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const definePolicy = (input) => {
|
|
2
4
|
return { on: input.on, table: input.table, when: input.when };
|
|
3
5
|
};
|
|
@@ -13,7 +15,8 @@ const definePolicies = (policies) => {
|
|
|
13
15
|
const key = JSON.stringify([policy.table, policy.on]);
|
|
14
16
|
const whens = seenWhenByKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
15
17
|
if (whens.has(policy.when)) {
|
|
16
|
-
throw new
|
|
18
|
+
throw new LunoraError$1(
|
|
19
|
+
"INTERNAL",
|
|
17
20
|
`definePolicies: duplicate policy for (table "${policy.table}", on "${policy.on}") — the same decision function is registered more than once. Multiple distinct policies per (table, on) are allowed (reads OR, writes AND); remove the duplicate.`
|
|
18
21
|
);
|
|
19
22
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
+
|
|
3
|
+
const NON_SECRET_BINDING_METHODS = [
|
|
4
|
+
"put",
|
|
5
|
+
// KV / R2
|
|
6
|
+
"list",
|
|
7
|
+
// KV / R2
|
|
8
|
+
"delete",
|
|
9
|
+
// KV / R2
|
|
10
|
+
"getWithMetadata",
|
|
11
|
+
// KV
|
|
12
|
+
"head",
|
|
13
|
+
// R2
|
|
14
|
+
"createMultipartUpload",
|
|
15
|
+
// R2
|
|
16
|
+
"idFromName",
|
|
17
|
+
// Durable Object namespace
|
|
18
|
+
"newUniqueId",
|
|
19
|
+
// Durable Object namespace
|
|
20
|
+
"getByName",
|
|
21
|
+
// Durable Object namespace
|
|
22
|
+
"send",
|
|
23
|
+
// Queue producer
|
|
24
|
+
"sendBatch",
|
|
25
|
+
// Queue producer
|
|
26
|
+
"writeDataPoint",
|
|
27
|
+
// Analytics Engine dataset
|
|
28
|
+
"create",
|
|
29
|
+
// Workflows binding (get(id) + create/createBatch)
|
|
30
|
+
"createBatch"
|
|
31
|
+
// Workflows binding
|
|
32
|
+
];
|
|
33
|
+
const isSecretBinding = (value) => {
|
|
34
|
+
if (typeof value !== "object" || value === null) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const record = value;
|
|
38
|
+
if (typeof record.get !== "function") {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return !NON_SECRET_BINDING_METHODS.some((method) => typeof record[method] === "function");
|
|
42
|
+
};
|
|
43
|
+
const createSecrets = (env) => {
|
|
44
|
+
return {
|
|
45
|
+
get: async (name) => {
|
|
46
|
+
const binding = env[name];
|
|
47
|
+
if (!isSecretBinding(binding)) {
|
|
48
|
+
throw new LunoraError$1(
|
|
49
|
+
"INTERNAL",
|
|
50
|
+
`ctx.secrets: no Secrets Store binding named "${name}". Add a \`secrets_store_secrets[]\` entry (binding "${name}", pointing at your store + secret) to wrangler.jsonc — \`ctx.secrets\` reads a Secrets Store binding, not a plain \`.dev.vars\` value.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return binding.get();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { createSecrets };
|