@lunora/server 1.0.0-alpha.30 → 1.0.0-alpha.32
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/data-model.mjs +0 -1
- package/dist/drizzle.mjs +1 -1
- package/dist/index.d.mts +105 -8
- package/dist/index.d.ts +105 -8
- package/dist/index.mjs +1 -29
- package/dist/packem_shared/LunoraEnvError-CgpI2Mm_.mjs +3 -0
- package/dist/packem_shared/LunoraError-LVhdU0Lo.mjs +1 -0
- package/dist/packem_shared/PRESENCE_DEFAULT_TTL_MS-CF_9uiu1.mjs +1 -0
- package/dist/packem_shared/allowAll-BnyNbJZT.mjs +1 -0
- package/dist/packem_shared/asBucketStorage-1pFfH-Tn.mjs +1 -0
- package/dist/packem_shared/bindOrm-Bp9hsM2q.mjs +1 -0
- package/dist/packem_shared/buildRlsReadRegistry-WdiqSj87.mjs +1 -0
- package/dist/packem_shared/composePluginMiddleware-COr09CXA.mjs +1 -0
- package/dist/packem_shared/createPolicyDsl-sV1swpkD.mjs +1 -0
- package/dist/packem_shared/createSecrets-CgVPiW2C.mjs +1 -0
- package/dist/packem_shared/defineAggregateIndex-CYuH6IbJ.mjs +1 -0
- package/dist/packem_shared/defineIdentity-B7gfAgxx.mjs +1 -0
- package/dist/packem_shared/defineMigration-Bfpwxv2f.mjs +1 -0
- package/dist/packem_shared/defineMutator-8Kz0j1Kt.mjs +1 -0
- package/dist/packem_shared/defineShape-CyuRfwHz.mjs +1 -0
- package/dist/packem_shared/defineStorageRule-BDu01PUn.mjs +1 -0
- package/dist/packem_shared/functions-CDC08CWY.mjs +1 -0
- package/dist/packem_shared/httpAction-C14NuF3V.mjs +4 -0
- package/dist/packem_shared/initLunora-CHh1EmR4.mjs +1 -0
- package/dist/packem_shared/mask-C6Bi78qj.mjs +1 -0
- package/dist/packem_shared/onConnect-CEtRmUpJ.mjs +1 -0
- package/dist/packem_shared/policy-tag-Dprt9JWo.mjs +1 -0
- package/dist/packem_shared/protectPublic-BhKewPqm.mjs +1 -0
- package/dist/packem_shared/rls-_iVsPvhX.mjs +1 -0
- package/dist/packem_shared/run-middleware-BeEEqmdE.mjs +1 -0
- package/dist/packem_shared/storageRules-BptPZbi8.mjs +1 -0
- package/dist/rls/testing.mjs +1 -49
- package/dist/types.d.mts +76 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.mjs +1 -31
- package/package.json +4 -4
- package/dist/packem_shared/LunoraEnvError-BGmd1Qs0.mjs +0 -187
- package/dist/packem_shared/LunoraError-WbxmrpxR.mjs +0 -9
- package/dist/packem_shared/PRESENCE_DEFAULT_TTL_MS-Cr0i4mTv.mjs +0 -114
- package/dist/packem_shared/asBucketStorage-Cnxd9y2q.mjs +0 -11
- package/dist/packem_shared/bindOrm-CaY7Wq9Z.mjs +0 -147
- package/dist/packem_shared/buildRlsReadRegistry-2uk_GfiH.mjs +0 -107
- package/dist/packem_shared/composePluginMiddleware-z62dttBo.mjs +0 -103
- package/dist/packem_shared/createPolicyDsl-By3QB4he.mjs +0 -32
- package/dist/packem_shared/createSecrets-DwaR2rNG.mjs +0 -58
- package/dist/packem_shared/defineAggregateIndex-cdo0g-un.mjs +0 -349
- package/dist/packem_shared/defineIdentity-DiX4zM9x.mjs +0 -35
- package/dist/packem_shared/defineMigration-Hx01yIht.mjs +0 -10
- package/dist/packem_shared/defineMutator-EIXAWhs9.mjs +0 -11
- package/dist/packem_shared/defineShape-C5scNOrf.mjs +0 -18
- package/dist/packem_shared/defineStorageRule-B5nL4Z1P.mjs +0 -23
- package/dist/packem_shared/functions-Di9FUNkf.mjs +0 -5
- package/dist/packem_shared/httpAction-DCXoYPIk.mjs +0 -355
- package/dist/packem_shared/initLunora-D0Wuki7S.mjs +0 -115
- package/dist/packem_shared/mask-BepaW7YN.mjs +0 -334
- package/dist/packem_shared/onConnect-CIPXKPyw.mjs +0 -13
- package/dist/packem_shared/policy-tag-DvpVH2tv.mjs +0 -13
- package/dist/packem_shared/protectPublic-BlcGpiRc.mjs +0 -15
- package/dist/packem_shared/rls-BaDQf7MG.mjs +0 -595
- package/dist/packem_shared/run-middleware-I6EiQfxL.mjs +0 -20
- package/dist/packem_shared/storageRules-6QxzDOcx.mjs +0 -88
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
-
|
|
3
|
-
const isUniqueConflict = (error) => typeof error === "object" && error !== null && error.code === "CONFLICT" && error.kind === "unique";
|
|
4
|
-
const buildUpsertWhere = (tableName, target, create) => {
|
|
5
|
-
const fields = typeof target === "string" ? [target] : target;
|
|
6
|
-
if (fields.length === 0) {
|
|
7
|
-
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: "target" must name at least one field`);
|
|
8
|
-
}
|
|
9
|
-
const where = {};
|
|
10
|
-
for (const field of fields) {
|
|
11
|
-
if (!(field in create)) {
|
|
12
|
-
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: target field "${field}" is missing from the create document`);
|
|
13
|
-
}
|
|
14
|
-
where[field] = create[field];
|
|
15
|
-
}
|
|
16
|
-
return where;
|
|
17
|
-
};
|
|
18
|
-
const bindTableFacade = (writer, tableName) => {
|
|
19
|
-
const insert = async (document, options) => {
|
|
20
|
-
if (options?.skipDuplicates !== true) {
|
|
21
|
-
return writer.insert(tableName, document);
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
return await writer.insert(tableName, document);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
if (isUniqueConflict(error)) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
const upsert = async ({ create, target, update }) => {
|
|
33
|
-
const where = buildUpsertWhere(tableName, target, create);
|
|
34
|
-
const existing = await writer.findFirst(tableName, { where });
|
|
35
|
-
if (existing && typeof existing["_id"] === "string") {
|
|
36
|
-
await writer.patch(existing["_id"], update ?? create, tableName);
|
|
37
|
-
return { created: false, id: existing["_id"] };
|
|
38
|
-
}
|
|
39
|
-
const id = await writer.insert(tableName, create);
|
|
40
|
-
return { created: true, id };
|
|
41
|
-
};
|
|
42
|
-
return {
|
|
43
|
-
aggregate: (options) => writer.aggregate(tableName, options),
|
|
44
|
-
count: (where) => writer.count(tableName, where),
|
|
45
|
-
delete: (id) => writer.delete(id, tableName),
|
|
46
|
-
// `deleteMany`/`patchMany` forward the bound `tableName` as `expectedTable`
|
|
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`);
|
|
62
|
-
}
|
|
63
|
-
const whereArgs = first;
|
|
64
|
-
return writer.deleteWhere(tableName, whereArgs.where, { limit: whereArgs.limit });
|
|
65
|
-
},
|
|
66
|
-
// `exists` reuses `findFirst` (RLS-filtered, indexed when a `.withIndex`-able
|
|
67
|
-
// `where` is supplied) and only asks whether a row came back — no count scan.
|
|
68
|
-
exists: async (where) => await writer.findFirst(tableName, where === void 0 ? void 0 : { where }) !== null,
|
|
69
|
-
findFirst: (args) => writer.findFirst(tableName, args),
|
|
70
|
-
findFirstOrThrow: (args) => writer.findFirstOrThrow(tableName, args),
|
|
71
|
-
findMany: (args) => writer.findMany(tableName, args),
|
|
72
|
-
get: (id) => writer.get(id, tableName),
|
|
73
|
-
groupBy: (options) => writer.groupBy(tableName, options),
|
|
74
|
-
// Physical removal — bypasses `.softDelete()`. RLS gates it as a delete.
|
|
75
|
-
hardDelete: (id) => writer.delete(id, tableName, { hard: true }),
|
|
76
|
-
insert,
|
|
77
|
-
insertMany: (documents, options) => {
|
|
78
|
-
if (writer.insertMany === void 0) {
|
|
79
|
-
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.insertMany is unavailable: this writer has no batch insert`);
|
|
80
|
-
}
|
|
81
|
-
return writer.insertMany(tableName, documents, options);
|
|
82
|
-
},
|
|
83
|
-
patch: (id, patch) => writer.patch(id, patch, tableName),
|
|
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`);
|
|
99
|
-
}
|
|
100
|
-
const whereArgs = first;
|
|
101
|
-
return writer.patchWhere(tableName, { patch: whereArgs.values, where: whereArgs.where }, { limit: whereArgs.limit });
|
|
102
|
-
},
|
|
103
|
-
rank: (indexName, options) => writer.rank(tableName, indexName, options),
|
|
104
|
-
rankPage: (indexName, options) => writer.rankPage(tableName, indexName, options),
|
|
105
|
-
replace: (id, document) => writer.replace(id, document, tableName),
|
|
106
|
-
restore: async (id) => {
|
|
107
|
-
if (!writer.restore) {
|
|
108
|
-
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.restore is unavailable: this writer has no restore (is the table .softDelete()?)`);
|
|
109
|
-
}
|
|
110
|
-
await writer.restore(id, tableName);
|
|
111
|
-
},
|
|
112
|
-
upsert,
|
|
113
|
-
upsertMany: async ({ rows, target }) => {
|
|
114
|
-
const results = [];
|
|
115
|
-
for (const row of rows) {
|
|
116
|
-
results.push(await upsert({ create: row.create, target, update: row.update }));
|
|
117
|
-
}
|
|
118
|
-
return results;
|
|
119
|
-
},
|
|
120
|
-
withGeoIndex: (indexName, build) => writer.query(tableName).withGeoIndex(indexName, build),
|
|
121
|
-
withSearchIndex: (indexName, search) => writer.query(tableName).withSearchIndex(indexName, search)
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
const bindOrm = (facade) => {
|
|
125
|
-
const resolve = (table) => {
|
|
126
|
-
const bound = facade[table];
|
|
127
|
-
if (!bound) {
|
|
128
|
-
throw new LunoraError$1("INTERNAL", `unknown table: ${table}`);
|
|
129
|
-
}
|
|
130
|
-
return bound;
|
|
131
|
-
};
|
|
132
|
-
return {
|
|
133
|
-
delete: (table, id) => resolve(table).delete(id),
|
|
134
|
-
insert: (table) => {
|
|
135
|
-
return { values: (document) => resolve(table).insert(document) };
|
|
136
|
-
},
|
|
137
|
-
query: facade,
|
|
138
|
-
replace: (table, id) => {
|
|
139
|
-
return { with: (document) => resolve(table).replace(id, document) };
|
|
140
|
-
},
|
|
141
|
-
update: (table, id) => {
|
|
142
|
-
return { set: (values) => resolve(table).patch(id, values) };
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export { bindOrm, bindTableFacade };
|
|
@@ -1,107 +0,0 @@
|
|
|
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,103 +0,0 @@
|
|
|
1
|
-
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
-
import { r as runMiddlewareChain } from './run-middleware-I6EiQfxL.mjs';
|
|
3
|
-
|
|
4
|
-
const prefixTableName = (key, bareName) => `${key}_${bareName}`;
|
|
5
|
-
const rewriteReference = (target, key, bareNames) => bareNames.has(target) ? prefixTableName(key, target) : target;
|
|
6
|
-
const rewriteTableReferences = (table, key, bareNames, ownBareName) => {
|
|
7
|
-
const ownPrefixed = prefixTableName(key, ownBareName);
|
|
8
|
-
const rewriteOn = (on) => on === "" ? ownPrefixed : rewriteReference(on, key, bareNames);
|
|
9
|
-
const relationMap = {};
|
|
10
|
-
for (const [accessor, relation] of Object.entries(table.relationMap)) {
|
|
11
|
-
relationMap[accessor] = { ...relation, table: rewriteReference(relation.table, key, bareNames) };
|
|
12
|
-
}
|
|
13
|
-
const aggregateIndexes = table.aggregateIndexes.map((index) => {
|
|
14
|
-
return { ...index, on: rewriteOn(index.on) };
|
|
15
|
-
});
|
|
16
|
-
const rankIndexes = table.rankIndexes.map((index) => {
|
|
17
|
-
return { ...index, on: rewriteOn(index.on) };
|
|
18
|
-
});
|
|
19
|
-
return { ...table, aggregateIndexes, rankIndexes, relationMap };
|
|
20
|
-
};
|
|
21
|
-
const defineSchemaExtension = (key, options) => {
|
|
22
|
-
if (!key) {
|
|
23
|
-
throw new LunoraError$1("INTERNAL", "defineSchemaExtension: `key` is required and must be a non-empty string");
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
key,
|
|
27
|
-
tables: options.tables,
|
|
28
|
-
...options.vectorIndexes ? { vectorIndexes: options.vectorIndexes } : {}
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
const definePlugin = ((key, options) => {
|
|
32
|
-
if (!key) {
|
|
33
|
-
throw new LunoraError$1("INTERNAL", "definePlugin: `key` is required and must be a non-empty string");
|
|
34
|
-
}
|
|
35
|
-
if (options.extension && options.extension.key !== key) {
|
|
36
|
-
throw new LunoraError$1("INTERNAL", `definePlugin("${key}"): extension key "${options.extension.key}" does not match plugin key`);
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
key,
|
|
40
|
-
...options.extension ? { extension: options.extension } : {},
|
|
41
|
-
...options.middleware ? { middleware: options.middleware } : {}
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
const defineComponent = (key, options) => {
|
|
45
|
-
const plugin = definePlugin(key, {
|
|
46
|
-
...options.extension ? { extension: options.extension } : {},
|
|
47
|
-
...options.middleware ? { middleware: options.middleware } : {}
|
|
48
|
-
});
|
|
49
|
-
return {
|
|
50
|
-
...plugin,
|
|
51
|
-
functions: options.functions ?? {}
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
const mergeSchemaExtension = (base, extension) => {
|
|
55
|
-
const { key } = extension;
|
|
56
|
-
const bareNames = new Set(Object.keys(extension.tables));
|
|
57
|
-
const merged = { ...base.tables };
|
|
58
|
-
for (const [bareName, table] of Object.entries(extension.tables)) {
|
|
59
|
-
const prefixed = prefixTableName(key, bareName);
|
|
60
|
-
if (Object.hasOwn(merged, prefixed)) {
|
|
61
|
-
throw new LunoraError$1(
|
|
62
|
-
"INTERNAL",
|
|
63
|
-
`defineSchema(...).extend("${key}"): table "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
merged[prefixed] = rewriteTableReferences(table, key, bareNames, bareName);
|
|
67
|
-
}
|
|
68
|
-
const mergedVectorIndexes = { ...base.vectorIndexes };
|
|
69
|
-
if (extension.vectorIndexes) {
|
|
70
|
-
for (const [bareIndexName, index] of Object.entries(extension.vectorIndexes)) {
|
|
71
|
-
const prefixed = prefixTableName(key, bareIndexName);
|
|
72
|
-
if (Object.hasOwn(mergedVectorIndexes, prefixed)) {
|
|
73
|
-
throw new LunoraError$1(
|
|
74
|
-
"INTERNAL",
|
|
75
|
-
`defineSchema(...).extend("${key}"): vector index "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
mergedVectorIndexes[prefixed] = { ...index, table: rewriteReference(index.table, key, bareNames) };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
// Preserve secure-by-default RLS across `.extend(...)` — a plugin's
|
|
83
|
-
// tables join an `.rls("required")` schema as protected-by-default too.
|
|
84
|
-
rlsMode: base.rlsMode,
|
|
85
|
-
tables: merged,
|
|
86
|
-
vectorIndexes: mergedVectorIndexes
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
const installPlugins = (base, plugins) => {
|
|
90
|
-
let schema = base;
|
|
91
|
-
for (const plugin of plugins) {
|
|
92
|
-
if (plugin.extension) {
|
|
93
|
-
schema = mergeSchemaExtension(schema, plugin.extension);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return schema;
|
|
97
|
-
};
|
|
98
|
-
const composePluginMiddleware = (plugins) => {
|
|
99
|
-
const middlewares = plugins.map((plugin) => plugin.middleware).filter((middleware) => middleware !== void 0);
|
|
100
|
-
return (async ({ ctx, next }) => runMiddlewareChain(middlewares, ctx, (context) => next({ ctx: context })));
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export { composePluginMiddleware, defineComponent, definePlugin, defineSchemaExtension, installPlugins, mergeSchemaExtension };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { LunoraError as LunoraError$1 } from '@lunora/errors';
|
|
2
|
-
|
|
3
|
-
const definePolicy = (input) => {
|
|
4
|
-
return { on: input.on, table: input.table, when: input.when };
|
|
5
|
-
};
|
|
6
|
-
const createPolicyDsl = () => (input) => {
|
|
7
|
-
return { on: input.on, table: input.table, when: input.when };
|
|
8
|
-
};
|
|
9
|
-
const definePermission = (name, options = {}) => {
|
|
10
|
-
return { name, ...options };
|
|
11
|
-
};
|
|
12
|
-
const definePolicies = (policies) => {
|
|
13
|
-
const seenWhenByKey = /* @__PURE__ */ new Map();
|
|
14
|
-
for (const policy of policies) {
|
|
15
|
-
const key = JSON.stringify([policy.table, policy.on]);
|
|
16
|
-
const whens = seenWhenByKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
17
|
-
if (whens.has(policy.when)) {
|
|
18
|
-
throw new LunoraError$1(
|
|
19
|
-
"INTERNAL",
|
|
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.`
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
whens.add(policy.when);
|
|
24
|
-
seenWhenByKey.set(key, whens);
|
|
25
|
-
}
|
|
26
|
-
return policies;
|
|
27
|
-
};
|
|
28
|
-
const defineRole = (name, options = {}) => {
|
|
29
|
-
return { name, ...options };
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole };
|
|
@@ -1,58 +0,0 @@
|
|
|
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 };
|