@airdraft/cloud 0.1.0
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/CHANGELOG.md +5 -0
- package/dist/billing.d.ts +72 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +204 -0
- package/dist/billing.js.map +1 -0
- package/dist/callback.d.ts +39 -0
- package/dist/callback.d.ts.map +1 -0
- package/dist/callback.js +178 -0
- package/dist/callback.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/db.d.ts +13 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +23 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/invite.d.ts +28 -0
- package/dist/invite.d.ts.map +1 -0
- package/dist/invite.js +155 -0
- package/dist/invite.js.map +1 -0
- package/dist/jwt.d.ts +9 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +32 -0
- package/dist/jwt.js.map +1 -0
- package/dist/project.d.ts +30 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +145 -0
- package/dist/project.js.map +1 -0
- package/dist/relay.d.ts +35 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +101 -0
- package/dist/relay.js.map +1 -0
- package/dist/runtime.d.ts +48 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +186 -0
- package/dist/runtime.js.map +1 -0
- package/dist/state.d.ts +40 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +58 -0
- package/dist/state.js.map +1 -0
- package/dist/team.d.ts +31 -0
- package/dist/team.d.ts.map +1 -0
- package/dist/team.js +150 -0
- package/dist/team.js.map +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook.d.ts +29 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +125 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +45 -0
package/dist/db.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a typed `CloudDb` handle from a connected `MongoClient`.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const client = new MongoClient(process.env.MONGODB_URI!)
|
|
6
|
+
* await client.connect()
|
|
7
|
+
* export const db = createCloudDb(client)
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export function createCloudDb(client, dbName = 'airdraft') {
|
|
11
|
+
const db = client.db(dbName);
|
|
12
|
+
return {
|
|
13
|
+
projects: db.collection('projects'),
|
|
14
|
+
teams: db.collection('teams'),
|
|
15
|
+
memberships: db.collection('memberships'),
|
|
16
|
+
invites: db.collection('invites'),
|
|
17
|
+
plans: db.collection('plans'),
|
|
18
|
+
customDomains: db.collection('customDomains'),
|
|
19
|
+
embedTokens: db.collection('embedTokens'),
|
|
20
|
+
auditEvents: db.collection('auditEvents'),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,MAAM,GAAG,UAAU;IACpE,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAA;IAC5B,OAAO;QACL,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAa,UAAU,CAAC;QAC/C,KAAK,EAAE,EAAE,CAAC,UAAU,CAAU,OAAO,CAAC;QACtC,WAAW,EAAE,EAAE,CAAC,UAAU,CAAgB,aAAa,CAAC;QACxD,OAAO,EAAE,EAAE,CAAC,UAAU,CAAY,SAAS,CAAC;QAC5C,KAAK,EAAE,EAAE,CAAC,UAAU,CAAU,OAAO,CAAC;QACtC,aAAa,EAAE,EAAE,CAAC,UAAU,CAAkB,eAAe,CAAC;QAC9D,WAAW,EAAE,EAAE,CAAC,UAAU,CAAgB,aAAa,CAAC;QACxD,WAAW,EAAE,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC;KAC1C,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type { CloudDb, TokenCache, GitHubConnection, ProjectDoc, TeamDoc, MembershipDoc, InviteDoc, CustomDomainDoc, EmbedTokenDoc, PlanDoc, AuditEventDoc } from './types.js';
|
|
2
|
+
export { createCloudDb } from './db.js';
|
|
3
|
+
export { signGitHubAppJwt } from './jwt.js';
|
|
4
|
+
export { signState, verifyState } from './state.js';
|
|
5
|
+
export type { StatePayload, StateVerifyResult } from './state.js';
|
|
6
|
+
export { handleTokenRelay } from './relay.js';
|
|
7
|
+
export type { RelayOptions } from './relay.js';
|
|
8
|
+
export { handleWebhook } from './webhook.js';
|
|
9
|
+
export type { WebhookOptions } from './webhook.js';
|
|
10
|
+
export { handleCallback } from './callback.js';
|
|
11
|
+
export type { CallbackOptions } from './callback.js';
|
|
12
|
+
export { handleListTeams, handleCreateTeam, handleGetTeam, handleUpdateTeam } from './team.js';
|
|
13
|
+
export { handleListProjects, handleCreateProject, handleGetProject, handleGenerateApiKey } from './project.js';
|
|
14
|
+
export { handleInviteMember, handleAcceptInvite, handleRevokeInvite } from './invite.js';
|
|
15
|
+
export { handleGetPlans, handleUpgradePlan, handleWebhookSettlement, handleGetUsage, checkLimit, checkFeature } from './billing.js';
|
|
16
|
+
export type { PayClient } from './billing.js';
|
|
17
|
+
export { createCloudCmsHandler, bustEngineCache } from './runtime.js';
|
|
18
|
+
export type { CloudCmsOptions, CloudCmsHandler } from './runtime.js';
|
|
19
|
+
export { signCliToken, verifyCliToken } from './cli.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC9K,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAE9F,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAE9G,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAExF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACnI,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEpE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { createCloudDb } from './db.js';
|
|
2
|
+
export { signGitHubAppJwt } from './jwt.js';
|
|
3
|
+
export { signState, verifyState } from './state.js';
|
|
4
|
+
export { handleTokenRelay } from './relay.js';
|
|
5
|
+
export { handleWebhook } from './webhook.js';
|
|
6
|
+
export { handleCallback } from './callback.js';
|
|
7
|
+
// Team management
|
|
8
|
+
export { handleListTeams, handleCreateTeam, handleGetTeam, handleUpdateTeam } from './team.js';
|
|
9
|
+
// Project management
|
|
10
|
+
export { handleListProjects, handleCreateProject, handleGetProject, handleGenerateApiKey } from './project.js';
|
|
11
|
+
// Invite management
|
|
12
|
+
export { handleInviteMember, handleAcceptInvite, handleRevokeInvite } from './invite.js';
|
|
13
|
+
// Billing
|
|
14
|
+
export { handleGetPlans, handleUpgradePlan, handleWebhookSettlement, handleGetUsage, checkLimit, checkFeature } from './billing.js';
|
|
15
|
+
// Cloud CMS runtime
|
|
16
|
+
export { createCloudCmsHandler, bustEngineCache } from './runtime.js';
|
|
17
|
+
// CLI tokens
|
|
18
|
+
export { signCliToken, verifyCliToken } from './cli.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,kBAAkB;AAClB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC9F,qBAAqB;AACrB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAC9G,oBAAoB;AACpB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACxF,UAAU;AACV,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEnI,oBAAoB;AACpB,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAErE,aAAa;AACb,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/invite.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CloudAuthAdapter } from '@airdraft/cloud-auth';
|
|
2
|
+
import type { CloudDb } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* POST /v1/teams/:teamSlug/invites
|
|
5
|
+
*
|
|
6
|
+
* Body: `{ email: string; role: 'admin' | 'editor' | 'viewer' }`
|
|
7
|
+
*
|
|
8
|
+
* Creates a pending invite. Requires `owner` or `admin` role.
|
|
9
|
+
* Caller is responsible for sending the invite email (email service wired at app level).
|
|
10
|
+
*/
|
|
11
|
+
export declare function handleInviteMember(req: Request, db: CloudDb, auth: CloudAuthAdapter, teamSlug: string): Promise<Response>;
|
|
12
|
+
/**
|
|
13
|
+
* POST /v1/invites/:token/accept
|
|
14
|
+
*
|
|
15
|
+
* Accepts an invite. If the user doesn't exist, the cloud app layer
|
|
16
|
+
* should call `auth.createUser()` before calling this handler.
|
|
17
|
+
*
|
|
18
|
+
* Creates a `MembershipDoc` and deletes the invite.
|
|
19
|
+
* The request must include the authenticated session of the accepting user.
|
|
20
|
+
*/
|
|
21
|
+
export declare function handleAcceptInvite(req: Request, db: CloudDb, auth: CloudAuthAdapter, token: string): Promise<Response>;
|
|
22
|
+
/**
|
|
23
|
+
* DELETE /v1/teams/:teamSlug/invites/:token
|
|
24
|
+
*
|
|
25
|
+
* Deletes a pending invite. Requires `owner` or `admin` role.
|
|
26
|
+
*/
|
|
27
|
+
export declare function handleRevokeInvite(req: Request, db: CloudDb, auth: CloudAuthAdapter, teamSlug: string, token: string): Promise<Response>;
|
|
28
|
+
//# sourceMappingURL=invite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invite.d.ts","sourceRoot":"","sources":["../src/invite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AA2BzC;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAmEnB;AAMD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CA0BnB;AAMD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAoBnB"}
|
package/dist/invite.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Helpers
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
function json(body, status = 200) {
|
|
6
|
+
return new Response(JSON.stringify(body), {
|
|
7
|
+
status,
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function err(code, message, status) {
|
|
12
|
+
return json({ error: { code, message } }, status);
|
|
13
|
+
}
|
|
14
|
+
const INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
15
|
+
function generateInviteToken() {
|
|
16
|
+
return randomBytes(24).toString('hex');
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// handleInviteMember
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* POST /v1/teams/:teamSlug/invites
|
|
23
|
+
*
|
|
24
|
+
* Body: `{ email: string; role: 'admin' | 'editor' | 'viewer' }`
|
|
25
|
+
*
|
|
26
|
+
* Creates a pending invite. Requires `owner` or `admin` role.
|
|
27
|
+
* Caller is responsible for sending the invite email (email service wired at app level).
|
|
28
|
+
*/
|
|
29
|
+
export async function handleInviteMember(req, db, auth, teamSlug) {
|
|
30
|
+
const identity = await auth.verifySession(req);
|
|
31
|
+
if (!identity)
|
|
32
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
33
|
+
const team = await db.teams.findOne({ slug: teamSlug });
|
|
34
|
+
if (!team)
|
|
35
|
+
return err('TEAM_NOT_FOUND', 'Team not found', 404);
|
|
36
|
+
const membership = await db.memberships.findOne({
|
|
37
|
+
teamId: team._id.toString(),
|
|
38
|
+
userId: identity.userId,
|
|
39
|
+
});
|
|
40
|
+
if (!membership || !['owner', 'admin'].includes(membership.role)) {
|
|
41
|
+
return err('FORBIDDEN', 'Requires owner or admin role', 403);
|
|
42
|
+
}
|
|
43
|
+
let body;
|
|
44
|
+
try {
|
|
45
|
+
body = await req.json();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return err('INVALID_BODY', 'Request body must be JSON', 400);
|
|
49
|
+
}
|
|
50
|
+
const email = typeof body.email === 'string' ? body.email.trim().toLowerCase() : '';
|
|
51
|
+
if (!email || !email.includes('@'))
|
|
52
|
+
return err('INVALID_EMAIL', 'Valid email is required', 400);
|
|
53
|
+
const allowedRoles = ['admin', 'editor', 'viewer'];
|
|
54
|
+
const role = allowedRoles.includes(body.role)
|
|
55
|
+
? body.role
|
|
56
|
+
: null;
|
|
57
|
+
if (!role)
|
|
58
|
+
return err('INVALID_ROLE', 'Role must be admin, editor, or viewer', 400);
|
|
59
|
+
// Check if already a member
|
|
60
|
+
const existingMember = await db.memberships.findOne({
|
|
61
|
+
teamId: team._id.toString(),
|
|
62
|
+
// We can't query by email directly — that's in the auth system. Skip for v1.
|
|
63
|
+
});
|
|
64
|
+
void existingMember; // suppress lint — full check requires auth user lookup, deferred to v1.x
|
|
65
|
+
// Check for existing pending invite
|
|
66
|
+
const existingInvite = await db.invites.findOne({
|
|
67
|
+
teamId: team._id.toString(),
|
|
68
|
+
email,
|
|
69
|
+
expiresAt: { $gt: new Date() },
|
|
70
|
+
});
|
|
71
|
+
if (existingInvite) {
|
|
72
|
+
return err('INVITE_EXISTS', 'A pending invite already exists for this email', 409);
|
|
73
|
+
}
|
|
74
|
+
const { ObjectId } = await import('mongodb');
|
|
75
|
+
const now = new Date();
|
|
76
|
+
const inviteId = new ObjectId();
|
|
77
|
+
const token = generateInviteToken();
|
|
78
|
+
await db.invites.insertOne({
|
|
79
|
+
_id: inviteId,
|
|
80
|
+
teamId: team._id.toString(),
|
|
81
|
+
email,
|
|
82
|
+
role,
|
|
83
|
+
invitedBy: identity.userId,
|
|
84
|
+
token,
|
|
85
|
+
expiresAt: new Date(now.getTime() + INVITE_TTL_MS),
|
|
86
|
+
createdAt: now,
|
|
87
|
+
});
|
|
88
|
+
const invite = await db.invites.findOne({ _id: inviteId });
|
|
89
|
+
return json({ data: invite }, 201);
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// handleAcceptInvite
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
/**
|
|
95
|
+
* POST /v1/invites/:token/accept
|
|
96
|
+
*
|
|
97
|
+
* Accepts an invite. If the user doesn't exist, the cloud app layer
|
|
98
|
+
* should call `auth.createUser()` before calling this handler.
|
|
99
|
+
*
|
|
100
|
+
* Creates a `MembershipDoc` and deletes the invite.
|
|
101
|
+
* The request must include the authenticated session of the accepting user.
|
|
102
|
+
*/
|
|
103
|
+
export async function handleAcceptInvite(req, db, auth, token) {
|
|
104
|
+
const identity = await auth.verifySession(req);
|
|
105
|
+
if (!identity)
|
|
106
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
107
|
+
const invite = await db.invites.findOne({ token });
|
|
108
|
+
if (!invite)
|
|
109
|
+
return err('INVITE_NOT_FOUND', 'Invite not found or already used', 404);
|
|
110
|
+
if (invite.expiresAt < new Date())
|
|
111
|
+
return err('INVITE_EXPIRED', 'Invite has expired', 410);
|
|
112
|
+
// Verify the accepting user's email matches the invite
|
|
113
|
+
if (identity.email.toLowerCase() !== invite.email) {
|
|
114
|
+
return err('EMAIL_MISMATCH', 'Authenticated email does not match invite', 403);
|
|
115
|
+
}
|
|
116
|
+
const { ObjectId } = await import('mongodb');
|
|
117
|
+
await db.memberships.insertOne({
|
|
118
|
+
_id: new ObjectId(),
|
|
119
|
+
teamId: invite.teamId,
|
|
120
|
+
userId: identity.userId,
|
|
121
|
+
role: invite.role,
|
|
122
|
+
createdAt: new Date(),
|
|
123
|
+
});
|
|
124
|
+
await db.invites.deleteOne({ _id: invite._id });
|
|
125
|
+
return json({ data: { teamId: invite.teamId, role: invite.role } });
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// handleRevokeInvite
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
/**
|
|
131
|
+
* DELETE /v1/teams/:teamSlug/invites/:token
|
|
132
|
+
*
|
|
133
|
+
* Deletes a pending invite. Requires `owner` or `admin` role.
|
|
134
|
+
*/
|
|
135
|
+
export async function handleRevokeInvite(req, db, auth, teamSlug, token) {
|
|
136
|
+
const identity = await auth.verifySession(req);
|
|
137
|
+
if (!identity)
|
|
138
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
139
|
+
const team = await db.teams.findOne({ slug: teamSlug });
|
|
140
|
+
if (!team)
|
|
141
|
+
return err('TEAM_NOT_FOUND', 'Team not found', 404);
|
|
142
|
+
const membership = await db.memberships.findOne({
|
|
143
|
+
teamId: team._id.toString(),
|
|
144
|
+
userId: identity.userId,
|
|
145
|
+
});
|
|
146
|
+
if (!membership || !['owner', 'admin'].includes(membership.role)) {
|
|
147
|
+
return err('FORBIDDEN', 'Requires owner or admin role', 403);
|
|
148
|
+
}
|
|
149
|
+
const invite = await db.invites.findOne({ token, teamId: team._id.toString() });
|
|
150
|
+
if (!invite)
|
|
151
|
+
return err('INVITE_NOT_FOUND', 'Invite not found', 404);
|
|
152
|
+
await db.invites.deleteOne({ _id: invite._id });
|
|
153
|
+
return new Response(null, { status: 204 });
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=invite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invite.js","sourceRoot":"","sources":["../src/invite.ts"],"names":[],"mappings":"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"}
|
package/dist/jwt.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signs a GitHub App JWT (RS256, 10-minute lifetime) using the app's private key.
|
|
3
|
+
*
|
|
4
|
+
* @param appId - GitHub App numeric ID (from app settings).
|
|
5
|
+
* @param privateKeyPem - PEM-encoded RSA private key (PKCS#1 or PKCS#8).
|
|
6
|
+
* @returns Signed JWT string for use as `Authorization: Bearer <jwt>`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function signGitHubAppJwt(appId: string, privateKeyPem: string): Promise<string>;
|
|
9
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB5F"}
|
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// GitHub App JWT signing using node:crypto (RS256).
|
|
2
|
+
// Produces a 10-minute JWT for authenticating as the GitHub App.
|
|
3
|
+
// Uses node:crypto createSign which accepts both PKCS#1 and PKCS#8 PEM keys.
|
|
4
|
+
import { createSign } from 'node:crypto';
|
|
5
|
+
function base64url(input) {
|
|
6
|
+
const buf = Buffer.isBuffer(input) ? input : Buffer.from(input);
|
|
7
|
+
return buf.toString('base64')
|
|
8
|
+
.replace(/\+/g, '-')
|
|
9
|
+
.replace(/\//g, '_')
|
|
10
|
+
.replace(/=+$/, '');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Signs a GitHub App JWT (RS256, 10-minute lifetime) using the app's private key.
|
|
14
|
+
*
|
|
15
|
+
* @param appId - GitHub App numeric ID (from app settings).
|
|
16
|
+
* @param privateKeyPem - PEM-encoded RSA private key (PKCS#1 or PKCS#8).
|
|
17
|
+
* @returns Signed JWT string for use as `Authorization: Bearer <jwt>`.
|
|
18
|
+
*/
|
|
19
|
+
export async function signGitHubAppJwt(appId, privateKeyPem) {
|
|
20
|
+
const now = Math.floor(Date.now() / 1000);
|
|
21
|
+
const header = base64url(Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })));
|
|
22
|
+
const payload = base64url(Buffer.from(JSON.stringify({ iat: now - 60, exp: now + 540, iss: appId })));
|
|
23
|
+
const signingInput = `${header}.${payload}`;
|
|
24
|
+
// Normalize literal \n sequences that survive .env parsing (dotenv quirk with some Next.js versions)
|
|
25
|
+
const normalizedKey = privateKeyPem.replace(/\\n/g, '\n');
|
|
26
|
+
// node:crypto createSign handles both PKCS#1 (RSA PRIVATE KEY) and PKCS#8 (PRIVATE KEY)
|
|
27
|
+
const sign = createSign('RSA-SHA256');
|
|
28
|
+
sign.update(signingInput);
|
|
29
|
+
const signature = sign.sign(normalizedKey);
|
|
30
|
+
return `${signingInput}.${base64url(signature)}`;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,iEAAiE;AACjE,6EAA6E;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,SAAS,SAAS,CAAC,KAA0B;IAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC/D,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC1B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AACvB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa,EAAE,aAAqB;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACzC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAA;IACnF,MAAM,OAAO,GAAG,SAAS,CACvB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAC3E,CAAA;IACD,MAAM,YAAY,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAA;IAE3C,qGAAqG;IACrG,MAAM,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAEzD,wFAAwF;IACxF,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CAAA;IACrC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACzB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAE1C,OAAO,GAAG,YAAY,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CloudAuthAdapter } from '@airdraft/cloud-auth';
|
|
2
|
+
import type { CloudDb } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* GET /v1/teams/:teamSlug/projects
|
|
5
|
+
*
|
|
6
|
+
* Returns all projects belonging to a team. Requires membership.
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleListProjects(req: Request, db: CloudDb, auth: CloudAuthAdapter, teamSlug: string): Promise<Response>;
|
|
9
|
+
/**
|
|
10
|
+
* POST /v1/teams/:teamSlug/projects
|
|
11
|
+
*
|
|
12
|
+
* Body: `{ name: string }`
|
|
13
|
+
*
|
|
14
|
+
* Creates a new project. Requires `owner` or `admin` role.
|
|
15
|
+
*/
|
|
16
|
+
export declare function handleCreateProject(req: Request, db: CloudDb, auth: CloudAuthAdapter, teamSlug: string): Promise<Response>;
|
|
17
|
+
/**
|
|
18
|
+
* GET /v1/projects/:projectSlug
|
|
19
|
+
*
|
|
20
|
+
* Returns project by slug. Resolves team membership from the project's teamId.
|
|
21
|
+
*/
|
|
22
|
+
export declare function handleGetProject(req: Request, db: CloudDb, auth: CloudAuthAdapter, projectSlug: string): Promise<Response>;
|
|
23
|
+
/**
|
|
24
|
+
* POST /v1/projects/:projectSlug/api-key
|
|
25
|
+
*
|
|
26
|
+
* Rotates the project API key. Returns the plaintext key ONCE.
|
|
27
|
+
* Stores only the SHA-256 hash. Requires `owner` or `admin` role.
|
|
28
|
+
*/
|
|
29
|
+
export declare function handleGenerateApiKey(req: Request, db: CloudDb, auth: CloudAuthAdapter, projectSlug: string): Promise<Response>;
|
|
30
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAmCzC;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAUnB;AAMD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CA2CnB;AAMD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC,CAcnB;AAMD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC,CAuBnB"}
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { generateApiKey } from '@airdraft/auth';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Helpers
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
function json(body, status = 200) {
|
|
6
|
+
return new Response(JSON.stringify(body), {
|
|
7
|
+
status,
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function err(code, message, status) {
|
|
12
|
+
return json({ error: { code, message } }, status);
|
|
13
|
+
}
|
|
14
|
+
async function resolveMembership(db, userId, teamSlug) {
|
|
15
|
+
const team = await db.teams.findOne({ slug: teamSlug });
|
|
16
|
+
if (!team)
|
|
17
|
+
return { team: null, membership: null };
|
|
18
|
+
const membership = await db.memberships.findOne({
|
|
19
|
+
teamId: team._id.toString(),
|
|
20
|
+
userId,
|
|
21
|
+
});
|
|
22
|
+
return { team, membership };
|
|
23
|
+
}
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// handleListProjects
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* GET /v1/teams/:teamSlug/projects
|
|
29
|
+
*
|
|
30
|
+
* Returns all projects belonging to a team. Requires membership.
|
|
31
|
+
*/
|
|
32
|
+
export async function handleListProjects(req, db, auth, teamSlug) {
|
|
33
|
+
const identity = await auth.verifySession(req);
|
|
34
|
+
if (!identity)
|
|
35
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
36
|
+
const { team, membership } = await resolveMembership(db, identity.userId, teamSlug);
|
|
37
|
+
if (!team)
|
|
38
|
+
return err('TEAM_NOT_FOUND', 'Team not found', 404);
|
|
39
|
+
if (!membership)
|
|
40
|
+
return err('FORBIDDEN', 'Not a member of this team', 403);
|
|
41
|
+
const projects = await db.projects.find({ teamId: team._id.toString() }).toArray();
|
|
42
|
+
return json({ data: projects });
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// handleCreateProject
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* POST /v1/teams/:teamSlug/projects
|
|
49
|
+
*
|
|
50
|
+
* Body: `{ name: string }`
|
|
51
|
+
*
|
|
52
|
+
* Creates a new project. Requires `owner` or `admin` role.
|
|
53
|
+
*/
|
|
54
|
+
export async function handleCreateProject(req, db, auth, teamSlug) {
|
|
55
|
+
const identity = await auth.verifySession(req);
|
|
56
|
+
if (!identity)
|
|
57
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
58
|
+
const { team, membership } = await resolveMembership(db, identity.userId, teamSlug);
|
|
59
|
+
if (!team)
|
|
60
|
+
return err('TEAM_NOT_FOUND', 'Team not found', 404);
|
|
61
|
+
if (!membership || !['owner', 'admin'].includes(membership.role)) {
|
|
62
|
+
return err('FORBIDDEN', 'Requires owner or admin role', 403);
|
|
63
|
+
}
|
|
64
|
+
let body;
|
|
65
|
+
try {
|
|
66
|
+
body = await req.json();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return err('INVALID_BODY', 'Request body must be JSON', 400);
|
|
70
|
+
}
|
|
71
|
+
const name = typeof body.name === 'string' ? body.name.trim() : '';
|
|
72
|
+
if (!name)
|
|
73
|
+
return err('INVALID_NAME', 'Project name is required', 400);
|
|
74
|
+
const baseSlug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
75
|
+
if (!baseSlug)
|
|
76
|
+
return err('INVALID_NAME', 'Project name must contain alphanumeric characters', 400);
|
|
77
|
+
const existing = await db.projects.findOne({ teamId: team._id.toString(), slug: baseSlug });
|
|
78
|
+
const slug = existing ? `${baseSlug}-${Date.now().toString(36)}` : baseSlug;
|
|
79
|
+
const { ObjectId } = await import('mongodb');
|
|
80
|
+
const now = new Date();
|
|
81
|
+
const projectId = new ObjectId();
|
|
82
|
+
await db.projects.insertOne({
|
|
83
|
+
_id: projectId,
|
|
84
|
+
teamId: team._id.toString(),
|
|
85
|
+
name,
|
|
86
|
+
slug,
|
|
87
|
+
apiKeyHash: null,
|
|
88
|
+
github: null,
|
|
89
|
+
createdAt: now,
|
|
90
|
+
updatedAt: now,
|
|
91
|
+
});
|
|
92
|
+
const project = await db.projects.findOne({ _id: projectId });
|
|
93
|
+
return json({ data: project }, 201);
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// handleGetProject
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/**
|
|
99
|
+
* GET /v1/projects/:projectSlug
|
|
100
|
+
*
|
|
101
|
+
* Returns project by slug. Resolves team membership from the project's teamId.
|
|
102
|
+
*/
|
|
103
|
+
export async function handleGetProject(req, db, auth, projectSlug) {
|
|
104
|
+
const identity = await auth.verifySession(req);
|
|
105
|
+
if (!identity)
|
|
106
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
107
|
+
const project = await db.projects.findOne({ slug: projectSlug });
|
|
108
|
+
if (!project)
|
|
109
|
+
return err('PROJECT_NOT_FOUND', 'Project not found', 404);
|
|
110
|
+
const membership = await db.memberships.findOne({
|
|
111
|
+
teamId: project.teamId,
|
|
112
|
+
userId: identity.userId,
|
|
113
|
+
});
|
|
114
|
+
if (!membership)
|
|
115
|
+
return err('FORBIDDEN', 'Not a member of this project team', 403);
|
|
116
|
+
return json({ data: project });
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// handleGenerateApiKey
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
/**
|
|
122
|
+
* POST /v1/projects/:projectSlug/api-key
|
|
123
|
+
*
|
|
124
|
+
* Rotates the project API key. Returns the plaintext key ONCE.
|
|
125
|
+
* Stores only the SHA-256 hash. Requires `owner` or `admin` role.
|
|
126
|
+
*/
|
|
127
|
+
export async function handleGenerateApiKey(req, db, auth, projectSlug) {
|
|
128
|
+
const identity = await auth.verifySession(req);
|
|
129
|
+
if (!identity)
|
|
130
|
+
return err('UNAUTHORIZED', 'Not authenticated', 401);
|
|
131
|
+
const project = await db.projects.findOne({ slug: projectSlug });
|
|
132
|
+
if (!project)
|
|
133
|
+
return err('PROJECT_NOT_FOUND', 'Project not found', 404);
|
|
134
|
+
const membership = await db.memberships.findOne({
|
|
135
|
+
teamId: project.teamId,
|
|
136
|
+
userId: identity.userId,
|
|
137
|
+
});
|
|
138
|
+
if (!membership || !['owner', 'admin'].includes(membership.role)) {
|
|
139
|
+
return err('FORBIDDEN', 'Requires owner or admin role', 403);
|
|
140
|
+
}
|
|
141
|
+
const { key, hash } = generateApiKey();
|
|
142
|
+
await db.projects.updateOne({ _id: project._id }, { $set: { apiKeyHash: hash, updatedAt: new Date() } });
|
|
143
|
+
return json({ data: { key } });
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
|
package/dist/relay.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CloudDb, TokenCache } from './types.js';
|
|
2
|
+
export interface RelayOptions {
|
|
3
|
+
/** GitHub App numeric ID — from `GITHUB_APP_ID` env var on the cloud server. */
|
|
4
|
+
githubAppId: string;
|
|
5
|
+
/** GitHub App PEM private key — from `GITHUB_APP_PRIVATE_KEY` env var. */
|
|
6
|
+
githubAppPrivateKey: string;
|
|
7
|
+
/** Optional L2 token cache (Upstash Redis in production). */
|
|
8
|
+
cache?: TokenCache;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Handler for `POST /v1/github/token`.
|
|
12
|
+
*
|
|
13
|
+
* Exchanges a project API key (`ntk_*`) for a GitHub installation access token.
|
|
14
|
+
* Used by mode 2 self-hosted runtimes that rely on Airdraft's shared GitHub App.
|
|
15
|
+
*
|
|
16
|
+
* Flow:
|
|
17
|
+
* 1. Extract and validate `ntk_*` key from `Authorization: Bearer` header
|
|
18
|
+
* 2. Hash → look up in `projects` collection
|
|
19
|
+
* 3. Apply per-installation rate limit (1 req/s, Redis counter)
|
|
20
|
+
* 4. Return cached token from Redis if still fresh
|
|
21
|
+
* 5. Mint fresh token via GitHub App JWT → cache → return
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* // In a Next.js route handler:
|
|
25
|
+
* export async function POST(req: Request) {
|
|
26
|
+
* return handleTokenRelay(req, db, {
|
|
27
|
+
* githubAppId: process.env.GITHUB_APP_ID!,
|
|
28
|
+
* githubAppPrivateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
|
|
29
|
+
* cache: redisCache,
|
|
30
|
+
* })
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleTokenRelay(req: Request, db: CloudDb, opts: RelayOptions): Promise<Response>;
|
|
35
|
+
//# sourceMappingURL=relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAMrD,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,mBAAmB,EAAE,MAAM,CAAA;IAC3B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB;AAsBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,QAAQ,CAAC,CAoEnB"}
|