sporades 0.8.1 → 0.8.3
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/CHANGES.md +13 -0
- package/bin/sporades-host-helper.js +1 -1
- package/bin/sporades.js +116 -13
- package/dist/cli/cli-version.d.ts +1 -1
- package/dist/cli/cli-version.js +1 -1
- package/dist/server-runtime-source.d.ts.map +1 -1
- package/dist/server-runtime-source.js +44 -2
- package/dist/server-runtime-source.js.map +1 -1
- package/dist/teams-runtime.d.ts +3 -2
- package/dist/teams-runtime.d.ts.map +1 -1
- package/dist/teams-runtime.js +75 -5
- package/dist/teams-runtime.js.map +1 -1
- package/dist/templates/client-runtime-template.js +3 -3
- package/package.json +7 -2
- package/src/types/client.d.ts +3 -2
- package/src/types/server.d.ts +26 -2
package/CHANGES.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## Unreleased - 2026-08-15
|
|
4
|
+
|
|
5
|
+
Changes since v0.8.1.
|
|
6
|
+
|
|
7
|
+
### 🚀 Features
|
|
8
|
+
|
|
9
|
+
- Add exact pagination and join admission (63f68a0).
|
|
10
|
+
|
|
11
|
+
### 🐛 Bug Fixes
|
|
12
|
+
|
|
13
|
+
- Resolve npm audit vulnerabilities (a6a4b51).
|
|
14
|
+
|
|
3
15
|
## 0.8.1 - 2026-08-14
|
|
4
16
|
|
|
5
17
|
Corrects the incomplete `0.8.0` package release with the merged `main` runtime,
|
|
@@ -16,3 +28,4 @@ generated artifacts, and documentation.
|
|
|
16
28
|
explicit Team decisions in table and File ACLs. Teams are built in but do
|
|
17
29
|
not select a current Team or automatically partition Capsule data; Sporades
|
|
18
30
|
never sends Join-link email. See the [Built-in Teams reference](https://mgscox.github.io/sporades/reference/teams).
|
|
31
|
+
|
|
@@ -418,7 +418,7 @@ function writeEnvelope(result, failed = false) {
|
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
// src/cli/cli-version.ts
|
|
421
|
-
var CLI_VERSION = "0.8.
|
|
421
|
+
var CLI_VERSION = "0.8.3";
|
|
422
422
|
|
|
423
423
|
// src/cli/schedule-inspection-envelope.ts
|
|
424
424
|
function sanitizeScheduleInspectionEnvelope(envelope, invalid) {
|
package/bin/sporades.js
CHANGED
|
@@ -68,8 +68,8 @@ export const teams = {
|
|
|
68
68
|
rename(teamId, name) {
|
|
69
69
|
return connect().teamsRename(teamId, name);
|
|
70
70
|
},
|
|
71
|
-
listMembers(teamId) {
|
|
72
|
-
return connect().teamsListMembers(teamId);
|
|
71
|
+
listMembers(teamId, options = {}) {
|
|
72
|
+
return connect().teamsListMembers(teamId, options);
|
|
73
73
|
},
|
|
74
74
|
updateApplicationRoles(teamId, userId, changes) {
|
|
75
75
|
return connect().teamsUpdateApplicationRoles(teamId, userId, changes);
|
|
@@ -1117,7 +1117,7 @@ function createConnection() {
|
|
|
1117
1117
|
teamsList() { return request("teams.list"); },
|
|
1118
1118
|
teamsCreate(name) { return request("teams.create", { name }); },
|
|
1119
1119
|
teamsRename(teamId, name) { return request("teams.rename", { teamId, name }); },
|
|
1120
|
-
teamsListMembers(teamId) { return request("teams.listMembers", { teamId }); },
|
|
1120
|
+
teamsListMembers(teamId, options = {}) { return request("teams.listMembers", { teamId, cursor: options.cursor, limit: options.limit }); },
|
|
1121
1121
|
teamsUpdateApplicationRoles(teamId, userId, changes) { return request("teams.updateApplicationRoles", { teamId, userId, add: changes?.add, remove: changes?.remove }); },
|
|
1122
1122
|
teamsCreateJoinLink(teamId, email, options = {}) { return request("teams.createJoinLink", { teamId, email, ttlSeconds: options.ttlSeconds }); },
|
|
1123
1123
|
teamsListJoinLinks(teamId) { return request("teams.listJoinLinks", { teamId }); },
|
|
@@ -8477,6 +8477,7 @@ var INITIAL_TEAM_NAME = "My Team";
|
|
|
8477
8477
|
var TEAM_NAME_MAX_BYTES = 80;
|
|
8478
8478
|
var TEAM_MEMBER_COUNT_MAX = 99;
|
|
8479
8479
|
var TEAM_MEMBER_LIST_MAX = 100;
|
|
8480
|
+
var TEAM_MEMBER_LIST_DEFAULT = TEAM_MEMBER_LIST_MAX;
|
|
8480
8481
|
var TEAM_MEMBERSHIP_MAX = 25;
|
|
8481
8482
|
var TEAM_BOOTSTRAP_RETRY_LIMIT = 5;
|
|
8482
8483
|
var TEAM_JOIN_LINK_DEFAULT_TTL_SECONDS = 60 * 60 * 24;
|
|
@@ -8538,9 +8539,9 @@ function createCurrentUserTeamsApi(database, auth, contextGetter) {
|
|
|
8538
8539
|
requireAuth({ auth }, { linked: true });
|
|
8539
8540
|
return renameCurrentUserTeam(database, auth, teamId, name, contextGetter?.());
|
|
8540
8541
|
},
|
|
8541
|
-
async listMembers(teamId) {
|
|
8542
|
+
async listMembers(teamId, options = {}) {
|
|
8542
8543
|
requireAuth({ auth }, { linked: true });
|
|
8543
|
-
return listTeamMembers(database, auth, teamId);
|
|
8544
|
+
return listTeamMembers(database, auth, teamId, options);
|
|
8544
8545
|
},
|
|
8545
8546
|
async updateApplicationRoles(teamId, userId, changes) {
|
|
8546
8547
|
requireAuth({ auth }, { linked: true });
|
|
@@ -8806,6 +8807,7 @@ async function joinCurrentUserTeam(database, auth, code, eventContext) {
|
|
|
8806
8807
|
"SELECT [role] FROM [sporades_team_memberships] WHERE [teamId] = ? AND [userId] = ?"
|
|
8807
8808
|
)).get(row.teamId, auth.userId);
|
|
8808
8809
|
if (!membership) {
|
|
8810
|
+
await enforceTeamJoinAdmission(database, tx, auth, String(row.teamId));
|
|
8809
8811
|
await ensureMembershipCounterOnAdapter(tx, auth.userId);
|
|
8810
8812
|
const claim = await tx.prepare(sql(
|
|
8811
8813
|
"UPDATE [sporades_team_membership_counters] SET [membershipCount] = [membershipCount] + 1 WHERE [userId] = ? AND [membershipCount] < ?"
|
|
@@ -8829,6 +8831,23 @@ async function joinCurrentUserTeam(database, auth, code, eventContext) {
|
|
|
8829
8831
|
emitTeamSecurityEvent(database, eventContext, "teams.joined", auth.userId, joined.id, "succeeded", "TEAM_JOINED");
|
|
8830
8832
|
return { team: joined };
|
|
8831
8833
|
}
|
|
8834
|
+
async function enforceTeamJoinAdmission(database, tx, auth, teamId) {
|
|
8835
|
+
if (typeof database.teamJoinAdmission !== "function") return;
|
|
8836
|
+
const count = await tx.prepare(tx.dialect.sql(
|
|
8837
|
+
"SELECT COUNT(*) AS [count] FROM [sporades_team_memberships] WHERE [teamId] = ?"
|
|
8838
|
+
)).get(teamId);
|
|
8839
|
+
try {
|
|
8840
|
+
const context = database.createTeamJoinAdmissionContext(tx, auth);
|
|
8841
|
+
const decision = await database.teamJoinAdmission(context, {
|
|
8842
|
+
teamId,
|
|
8843
|
+
userId: auth.userId,
|
|
8844
|
+
currentMemberCount: Number(count?.count ?? 0)
|
|
8845
|
+
});
|
|
8846
|
+
if (decision?.allow !== true) throw teamJoinDenied();
|
|
8847
|
+
} catch {
|
|
8848
|
+
throw teamJoinDenied();
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8832
8851
|
function normalizeTeamJoinEmail(email) {
|
|
8833
8852
|
const normalized = String(email ?? "").trim().toLowerCase();
|
|
8834
8853
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized)) {
|
|
@@ -8949,6 +8968,9 @@ function teamJoinLinkLimitError() {
|
|
|
8949
8968
|
function invalidTeamJoinLink() {
|
|
8950
8969
|
return commandError2("Join link is invalid.", "Use a current Join link for this linked account.", "INVALID_JOIN_LINK");
|
|
8951
8970
|
}
|
|
8971
|
+
function teamJoinDenied() {
|
|
8972
|
+
return commandError2("Could not join this Team.", "Ask a Team administrator for access.", "TEAM_JOIN_DENIED");
|
|
8973
|
+
}
|
|
8952
8974
|
async function listCurrentUserTeams(database, auth) {
|
|
8953
8975
|
requireAuth({ auth }, { linked: true });
|
|
8954
8976
|
await ensureInitialTeam(database, auth);
|
|
@@ -9159,7 +9181,7 @@ async function deleteCurrentUserTeam(database, auth, teamId, eventContext) {
|
|
|
9159
9181
|
emitTeamSecurityEvent(database, eventContext, "teams.deleted", auth.userId, teamId, "succeeded", "TEAM_DELETED");
|
|
9160
9182
|
return { deleted: true };
|
|
9161
9183
|
}
|
|
9162
|
-
async function listTeamMembers(database, auth, teamId) {
|
|
9184
|
+
async function listTeamMembers(database, auth, teamId, options = {}) {
|
|
9163
9185
|
requireAuth({ auth }, { linked: true });
|
|
9164
9186
|
if (!isOpaqueTeamId(teamId)) throw teamDenied();
|
|
9165
9187
|
return withTeamTransaction(database, async (tx) => {
|
|
@@ -9168,20 +9190,57 @@ async function listTeamMembers(database, auth, teamId) {
|
|
|
9168
9190
|
"SELECT [role] FROM [sporades_team_memberships] WHERE [teamId] = ? AND [userId] = ?"
|
|
9169
9191
|
)).get(teamId, auth.userId);
|
|
9170
9192
|
if (callerMembership?.role !== "admin") throw teamDenied();
|
|
9193
|
+
const page = normalizeTeamMemberPage(options);
|
|
9194
|
+
const total = await tx.prepare(sql(
|
|
9195
|
+
"SELECT COUNT(*) AS [count] FROM [sporades_team_memberships] WHERE [teamId] = ?"
|
|
9196
|
+
)).get(teamId);
|
|
9197
|
+
const cursorClause = page.cursor ? "AND ([m].[createdAt] > ? OR ([m].[createdAt] = ? AND [m].[userId] > ?)) " : "";
|
|
9198
|
+
const params = page.cursor ? [teamId, page.cursor.createdAt, page.cursor.createdAt, page.cursor.userId, page.limit + 1] : [teamId, page.limit + 1];
|
|
9171
9199
|
const rows = await tx.prepare(sql(
|
|
9172
|
-
"SELECT [m].[userId], [u].[displayName], [u].[picture], [m].[role] FROM [sporades_team_memberships] [m] JOIN [sporades_auth_users] [u] ON [u].[id] = [m].[userId] WHERE [m].[teamId] = ? ORDER BY [m].[createdAt] ASC, [m].[userId] ASC LIMIT ?"
|
|
9173
|
-
)).all(
|
|
9200
|
+
"SELECT [m].[userId], [u].[displayName], [u].[picture], [m].[role] , [m].[createdAt] FROM [sporades_team_memberships] [m] JOIN [sporades_auth_users] [u] ON [u].[id] = [m].[userId] WHERE [m].[teamId] = ? " + cursorClause + "ORDER BY [m].[createdAt] ASC, [m].[userId] ASC LIMIT ?"
|
|
9201
|
+
)).all(...params);
|
|
9202
|
+
const hasMore = rows.length > page.limit;
|
|
9203
|
+
const pageRows = hasMore ? rows.slice(0, page.limit) : rows;
|
|
9204
|
+
const last = pageRows.at(-1);
|
|
9174
9205
|
return {
|
|
9175
|
-
members: await Promise.all(
|
|
9206
|
+
members: await Promise.all(pageRows.map(async (row) => ({
|
|
9176
9207
|
userId: String(row.userId),
|
|
9177
9208
|
displayName: String(row.displayName),
|
|
9178
9209
|
picture: typeof row.picture === "string" && row.picture.length > 0 ? row.picture : null,
|
|
9179
9210
|
role: row.role === "admin" ? "admin" : "member",
|
|
9180
9211
|
applicationRoles: await activeTeamApplicationRoles(tx, database.teamApplicationRoles, teamId, row.userId)
|
|
9181
|
-
})))
|
|
9212
|
+
}))),
|
|
9213
|
+
...hasMore && last ? { nextCursor: encodeTeamMemberCursor(String(last.createdAt), String(last.userId)) } : {},
|
|
9214
|
+
totalCount: Number(total?.count ?? 0)
|
|
9182
9215
|
};
|
|
9183
9216
|
});
|
|
9184
9217
|
}
|
|
9218
|
+
function normalizeTeamMemberPage(options) {
|
|
9219
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) throw invalidTeamMemberPage();
|
|
9220
|
+
const limit = options.limit === void 0 ? TEAM_MEMBER_LIST_DEFAULT : options.limit;
|
|
9221
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > TEAM_MEMBER_LIST_MAX) throw invalidTeamMemberPage();
|
|
9222
|
+
if (options.cursor === void 0) return { cursor: null, limit };
|
|
9223
|
+
if (typeof options.cursor !== "string" || options.cursor.length < 1 || options.cursor.length > 512) throw invalidTeamMemberPage();
|
|
9224
|
+
try {
|
|
9225
|
+
if (!/^[A-Za-z0-9_-]+$/.test(options.cursor)) throw invalidTeamMemberPage();
|
|
9226
|
+
const cursorBytes = Buffer.from(options.cursor, "base64url");
|
|
9227
|
+
if (cursorBytes.toString("base64url") !== options.cursor) throw invalidTeamMemberPage();
|
|
9228
|
+
const decoded = JSON.parse(cursorBytes.toString("utf8"));
|
|
9229
|
+
if (decoded?.v !== 1 || typeof decoded.createdAt !== "string" || !Number.isFinite(Date.parse(decoded.createdAt)) || typeof decoded.userId !== "string" || decoded.userId.length < 1 || decoded.userId.length > 256) {
|
|
9230
|
+
throw invalidTeamMemberPage();
|
|
9231
|
+
}
|
|
9232
|
+
return { cursor: { createdAt: decoded.createdAt, userId: decoded.userId }, limit };
|
|
9233
|
+
} catch (error) {
|
|
9234
|
+
if (error?.code === "INVALID_TEAM_MEMBER_PAGE") throw error;
|
|
9235
|
+
throw invalidTeamMemberPage();
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
function encodeTeamMemberCursor(createdAt, userId) {
|
|
9239
|
+
return Buffer.from(JSON.stringify({ v: 1, createdAt, userId }), "utf8").toString("base64url");
|
|
9240
|
+
}
|
|
9241
|
+
function invalidTeamMemberPage() {
|
|
9242
|
+
return commandError2("Team member page is invalid.", `Use a limit from 1 through ${TEAM_MEMBER_LIST_MAX} and a cursor returned by listMembers().`, "INVALID_TEAM_MEMBER_PAGE");
|
|
9243
|
+
}
|
|
9185
9244
|
async function ensureInitialTeam(database, auth) {
|
|
9186
9245
|
if (database.__transactionActive) {
|
|
9187
9246
|
return ensureInitialTeamOnAdapter(database.adapter, auth.userId);
|
|
@@ -14445,7 +14504,10 @@ function quoteIdentifier(identifier) {
|
|
|
14445
14504
|
// src/server-runtime-source.ts
|
|
14446
14505
|
async function openDevDatabase(databasePath, serverSource, serverEnv = {}, config = {}, capsuleDefinition = null, options = {}) {
|
|
14447
14506
|
if (capsuleDefinition?.teams !== void 0 && (!capsuleDefinition.teams || typeof capsuleDefinition.teams !== "object" || Array.isArray(capsuleDefinition.teams))) {
|
|
14448
|
-
throw commandError2("Invalid Capsule Teams declaration.", "Declare teams as { appRoles?: string[] }.", "INVALID_TEAM_APPLICATION_ROLES");
|
|
14507
|
+
throw commandError2("Invalid Capsule Teams declaration.", "Declare teams as { appRoles?: string[], admitJoin?: function }.", "INVALID_TEAM_APPLICATION_ROLES");
|
|
14508
|
+
}
|
|
14509
|
+
if (capsuleDefinition?.teams?.admitJoin !== void 0 && typeof capsuleDefinition.teams.admitJoin !== "function") {
|
|
14510
|
+
throw commandError2("Invalid Capsule Team admission policy.", "Declare teams.admitJoin as a server function.", "INVALID_TEAM_JOIN_ADMISSION");
|
|
14449
14511
|
}
|
|
14450
14512
|
const teamApplicationRoles = normalizeTeamApplicationRoles(capsuleDefinition?.teams?.appRoles);
|
|
14451
14513
|
if (capsuleDefinition?.files !== void 0 && (!capsuleDefinition.files || typeof capsuleDefinition.files !== "object" || Array.isArray(capsuleDefinition.files))) {
|
|
@@ -14532,6 +14594,10 @@ async function openDevDatabase(databasePath, serverSource, serverEnv = {}, confi
|
|
|
14532
14594
|
passwordResetConfig: resolvePasswordResetConfig(config),
|
|
14533
14595
|
teamJoinLinkConfig: resolveTeamJoinLinkConfig(config),
|
|
14534
14596
|
teamApplicationRoles,
|
|
14597
|
+
teamJoinAdmission: capsuleDefinition?.teams?.admitJoin,
|
|
14598
|
+
createTeamJoinAdmissionContext(transactionAdapter, auth) {
|
|
14599
|
+
return createTeamJoinAdmissionContext(createTransactionDatabase(database, transactionAdapter), auth);
|
|
14600
|
+
},
|
|
14535
14601
|
fileAcl,
|
|
14536
14602
|
securityPolicy: resolveRuntimeSecurityPolicy(config),
|
|
14537
14603
|
fileStorage,
|
|
@@ -15823,6 +15889,33 @@ function createEndpointDatabaseApi(database, contextGetter = null) {
|
|
|
15823
15889
|
database.schema.tables.map((table) => [table.name, createEndpointTableApi(database, table, {}, contextGetter)])
|
|
15824
15890
|
);
|
|
15825
15891
|
}
|
|
15892
|
+
function createEndpointReadOnlyDatabaseApi(database, contextGetter = null) {
|
|
15893
|
+
return Object.fromEntries(
|
|
15894
|
+
database.schema.tables.map((table) => [
|
|
15895
|
+
table.name,
|
|
15896
|
+
readOnlyEndpointTableApi(createEndpointTableApi(database, table, {}, contextGetter))
|
|
15897
|
+
])
|
|
15898
|
+
);
|
|
15899
|
+
}
|
|
15900
|
+
function readOnlyEndpointTableApi(tableApi) {
|
|
15901
|
+
return {
|
|
15902
|
+
where(fieldName, value) {
|
|
15903
|
+
return readOnlyEndpointTableApi(tableApi.where(fieldName, value));
|
|
15904
|
+
},
|
|
15905
|
+
orderBy(fieldName, direction = "asc") {
|
|
15906
|
+
return readOnlyEndpointTableApi(tableApi.orderBy(fieldName, direction));
|
|
15907
|
+
},
|
|
15908
|
+
limit(count) {
|
|
15909
|
+
return readOnlyEndpointTableApi(tableApi.limit(count));
|
|
15910
|
+
},
|
|
15911
|
+
get() {
|
|
15912
|
+
return tableApi.get();
|
|
15913
|
+
},
|
|
15914
|
+
all() {
|
|
15915
|
+
return tableApi.all();
|
|
15916
|
+
}
|
|
15917
|
+
};
|
|
15918
|
+
}
|
|
15826
15919
|
function createEndpointTableApi(database, table, query = {}, contextGetter = null) {
|
|
15827
15920
|
return {
|
|
15828
15921
|
insert(values) {
|
|
@@ -16626,7 +16719,7 @@ function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
16626
16719
|
}
|
|
16627
16720
|
if (message.type === "teams.listMembers") {
|
|
16628
16721
|
try {
|
|
16629
|
-
const data = await listTeamMembers(database, client.session.auth, message.teamId);
|
|
16722
|
+
const data = await listTeamMembers(database, client.session.auth, message.teamId, { cursor: message.cursor, limit: message.limit });
|
|
16630
16723
|
sendJson(client, { id: message.id ?? null, type: "teams.listMembers.result", data, error: null });
|
|
16631
16724
|
} catch (error) {
|
|
16632
16725
|
if (error?.sporadesAuthDenialLogData) emitAuthDeniedLog(database, { data: error.sporadesAuthDenialLogData });
|
|
@@ -17426,6 +17519,16 @@ function createMutationContext(database, auth) {
|
|
|
17426
17519
|
};
|
|
17427
17520
|
return context;
|
|
17428
17521
|
}
|
|
17522
|
+
function createTeamJoinAdmissionContext(database, auth) {
|
|
17523
|
+
const context = {
|
|
17524
|
+
auth,
|
|
17525
|
+
env: database.serverEnv,
|
|
17526
|
+
log: createEndpointLogger(database)
|
|
17527
|
+
};
|
|
17528
|
+
const holder = createContextHolder(context);
|
|
17529
|
+
context.db = createEndpointReadOnlyDatabaseApi(database, () => holder.current);
|
|
17530
|
+
return context;
|
|
17531
|
+
}
|
|
17429
17532
|
function createCurrentUserJobApi(database, contextGetter) {
|
|
17430
17533
|
return {
|
|
17431
17534
|
async enqueue(handlerName, payload, options = {}) {
|
|
@@ -23542,7 +23645,7 @@ jobs:
|
|
|
23542
23645
|
}
|
|
23543
23646
|
|
|
23544
23647
|
// src/cli/cli-version.ts
|
|
23545
|
-
var CLI_VERSION = "0.8.
|
|
23648
|
+
var CLI_VERSION = "0.8.3";
|
|
23546
23649
|
|
|
23547
23650
|
// src/cli/sporades.ts
|
|
23548
23651
|
var SUPPORTED_TEMPLATES = new Set(CLIENT_TEMPLATES);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.8.
|
|
1
|
+
export declare const CLI_VERSION = "0.8.3";
|
|
2
2
|
//# sourceMappingURL=cli-version.d.ts.map
|
package/dist/cli/cli-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-runtime-source.d.ts","sourceRoot":"","sources":["../src/server-runtime-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAA4C,MAAM,WAAW,CAAC;AAQ3G,OAAO,EAAY,oBAAoB,EAAuD,MAAM,SAAS,CAAC;AAE9G,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAkHhC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AAoBrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAmBlC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AA8BpC,cAAc,mBAAmB,CAAC;AAsBlC,cAAc,+BAA+B,CAAC;AAiC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AA2BnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAuBzC,cAAc,wBAAwB,CAAC;AAqBvC,cAAc,uBAAuB,CAAC;AAiCtC,cAAc,kBAAkB,CAAC;AA8BjC,cAAc,mBAAmB,CAAC;AAElC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACvC,KAAK,aAAa,GAAG,WAAW,CAAC;AACjC,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAqBrD,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,GAAG,EACjB,SAAS,GAAE,UAAe,EAC1B,MAAM,GAAE,aAAkB,EAC1B,iBAAiB,GAAE,GAAU,EAC7B,OAAO,GAAE,WAAgB,
|
|
1
|
+
{"version":3,"file":"server-runtime-source.d.ts","sourceRoot":"","sources":["../src/server-runtime-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -337,7 +337,10 @@ export * from "./http-runtime.js";
|
|
|
337
337
|
// imports what it calls, and esbuild follows the graph.
|
|
338
338
|
export async function openDevDatabase(databasePath, serverSource, serverEnv = {}, config = {}, capsuleDefinition = null, options = {}) {
|
|
339
339
|
if (capsuleDefinition?.teams !== undefined && (!capsuleDefinition.teams || typeof capsuleDefinition.teams !== "object" || Array.isArray(capsuleDefinition.teams))) {
|
|
340
|
-
throw commandError("Invalid Capsule Teams declaration.", "Declare teams as { appRoles?: string[] }.", "INVALID_TEAM_APPLICATION_ROLES");
|
|
340
|
+
throw commandError("Invalid Capsule Teams declaration.", "Declare teams as { appRoles?: string[], admitJoin?: function }.", "INVALID_TEAM_APPLICATION_ROLES");
|
|
341
|
+
}
|
|
342
|
+
if (capsuleDefinition?.teams?.admitJoin !== undefined && typeof capsuleDefinition.teams.admitJoin !== "function") {
|
|
343
|
+
throw commandError("Invalid Capsule Team admission policy.", "Declare teams.admitJoin as a server function.", "INVALID_TEAM_JOIN_ADMISSION");
|
|
341
344
|
}
|
|
342
345
|
const teamApplicationRoles = normalizeTeamApplicationRoles(capsuleDefinition?.teams?.appRoles);
|
|
343
346
|
if (capsuleDefinition?.files !== undefined && (!capsuleDefinition.files || typeof capsuleDefinition.files !== "object" || Array.isArray(capsuleDefinition.files))) {
|
|
@@ -427,6 +430,10 @@ export async function openDevDatabase(databasePath, serverSource, serverEnv = {}
|
|
|
427
430
|
passwordResetConfig: resolvePasswordResetConfig(config),
|
|
428
431
|
teamJoinLinkConfig: resolveTeamJoinLinkConfig(config),
|
|
429
432
|
teamApplicationRoles,
|
|
433
|
+
teamJoinAdmission: capsuleDefinition?.teams?.admitJoin,
|
|
434
|
+
createTeamJoinAdmissionContext(transactionAdapter, auth) {
|
|
435
|
+
return createTeamJoinAdmissionContext(createTransactionDatabase(database, transactionAdapter), auth);
|
|
436
|
+
},
|
|
430
437
|
fileAcl,
|
|
431
438
|
securityPolicy: resolveRuntimeSecurityPolicy(config),
|
|
432
439
|
fileStorage,
|
|
@@ -1778,6 +1785,31 @@ function endpointQueryFromUrl(requestUrl) {
|
|
|
1778
1785
|
export function createEndpointDatabaseApi(database, contextGetter = null) {
|
|
1779
1786
|
return Object.fromEntries(database.schema.tables.map((table) => [table.name, createEndpointTableApi(database, table, {}, contextGetter)]));
|
|
1780
1787
|
}
|
|
1788
|
+
function createEndpointReadOnlyDatabaseApi(database, contextGetter = null) {
|
|
1789
|
+
return Object.fromEntries(database.schema.tables.map((table) => [
|
|
1790
|
+
table.name,
|
|
1791
|
+
readOnlyEndpointTableApi(createEndpointTableApi(database, table, {}, contextGetter)),
|
|
1792
|
+
]));
|
|
1793
|
+
}
|
|
1794
|
+
function readOnlyEndpointTableApi(tableApi) {
|
|
1795
|
+
return {
|
|
1796
|
+
where(fieldName, value) {
|
|
1797
|
+
return readOnlyEndpointTableApi(tableApi.where(fieldName, value));
|
|
1798
|
+
},
|
|
1799
|
+
orderBy(fieldName, direction = "asc") {
|
|
1800
|
+
return readOnlyEndpointTableApi(tableApi.orderBy(fieldName, direction));
|
|
1801
|
+
},
|
|
1802
|
+
limit(count) {
|
|
1803
|
+
return readOnlyEndpointTableApi(tableApi.limit(count));
|
|
1804
|
+
},
|
|
1805
|
+
get() {
|
|
1806
|
+
return tableApi.get();
|
|
1807
|
+
},
|
|
1808
|
+
all() {
|
|
1809
|
+
return tableApi.all();
|
|
1810
|
+
},
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1781
1813
|
function createEndpointTableApi(database, table, query = {}, contextGetter = null) {
|
|
1782
1814
|
return {
|
|
1783
1815
|
insert(values) {
|
|
@@ -2621,7 +2653,7 @@ export function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
2621
2653
|
}
|
|
2622
2654
|
if (message.type === "teams.listMembers") {
|
|
2623
2655
|
try {
|
|
2624
|
-
const data = await listTeamMembers(database, client.session.auth, message.teamId);
|
|
2656
|
+
const data = await listTeamMembers(database, client.session.auth, message.teamId, { cursor: message.cursor, limit: message.limit });
|
|
2625
2657
|
sendJson(client, { id: message.id ?? null, type: "teams.listMembers.result", data, error: null });
|
|
2626
2658
|
}
|
|
2627
2659
|
catch (error) {
|
|
@@ -3462,6 +3494,16 @@ function createMutationContext(database, auth) {
|
|
|
3462
3494
|
};
|
|
3463
3495
|
return context;
|
|
3464
3496
|
}
|
|
3497
|
+
function createTeamJoinAdmissionContext(database, auth) {
|
|
3498
|
+
const context = {
|
|
3499
|
+
auth,
|
|
3500
|
+
env: database.serverEnv,
|
|
3501
|
+
log: createEndpointLogger(database),
|
|
3502
|
+
};
|
|
3503
|
+
const holder = createContextHolder(context);
|
|
3504
|
+
context.db = createEndpointReadOnlyDatabaseApi(database, () => holder.current);
|
|
3505
|
+
return context;
|
|
3506
|
+
}
|
|
3465
3507
|
function createCurrentUserJobApi(database, contextGetter) {
|
|
3466
3508
|
return {
|
|
3467
3509
|
async enqueue(handlerName, payload, options = {}) {
|