@~lyre/auth 0.3.0 → 0.4.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/dist/admin.d.ts CHANGED
@@ -61,11 +61,25 @@ interface AppMember {
61
61
  via?: 'member' | 'role';
62
62
  }
63
63
  interface InviteResult {
64
- /** `added` = the email already had an Accounts user and was granted app access immediately;
65
- * `invited` = a new invite was created and an accept link emailed. */
66
- status: 'added' | 'invited';
64
+ /** `invited` = an invite was created and an accept link emailed;
65
+ * `already_member` = that email already has access to the app (returned with HTTP 409).
66
+ * (`added` is legacy: Accounts no longer grants access without acceptance.) */
67
+ status: 'invited' | 'already_member' | 'added';
68
+ /** The workspace the invitee joins on acceptance, echoed back; null when none was requested. */
69
+ userGroupId?: string | null;
67
70
  [key: string]: unknown;
68
71
  }
72
+ /** A pending (not yet accepted, not yet expired) app invitation. */
73
+ interface PendingInvite {
74
+ id: string;
75
+ email: string;
76
+ /** The workspace the invitee joins on acceptance; null for a group-less invite. */
77
+ userGroupId: string | null;
78
+ createdAt: string;
79
+ expiresAt: string;
80
+ /** The inviter's email, when still resolvable. */
81
+ invitedByEmail?: string | null;
82
+ }
69
83
  interface GroupMember {
70
84
  id: string;
71
85
  email: string;
@@ -106,11 +120,21 @@ declare class AccountsAdmin {
106
120
  confirmPassword?: string;
107
121
  revokeOtherSessions?: boolean;
108
122
  }): Promise<ChangePasswordResult>;
109
- /** Invite an email to an app: adds an existing Accounts user immediately, else emails an accept
110
- * link. Session-authed (caller must have access to the app). */
123
+ /**
124
+ * Invite an email to an app, emailing them an accept link. Session-authed (the caller must have
125
+ * access to the app).
126
+ *
127
+ * Pass `userGroupId` to say which WORKSPACE the invitee joins when they accept. Omit it and they
128
+ * join the app but no workspace — Accounts then treats them as a brand-new user on first login and
129
+ * sends them to create their own, instead of landing in the workspace that invited them. Accounts
130
+ * validates the group against the CALLER's own memberships, so this cannot target a foreign group.
131
+ */
111
132
  inviteToApp(appSlug: string, input: {
112
133
  email: string;
134
+ userGroupId?: string | null;
113
135
  }): Promise<InviteResult>;
136
+ /** Revoke a pending invite (withdraw one sent in error). Session-authed. */
137
+ revokeAppInvite(appSlug: string, inviteId: string): Promise<void>;
114
138
  /** Everyone with access to an app. Session-authed. */
115
139
  listAppUsers(appSlug: string): Promise<AppMember[]>;
116
140
  /** Revoke a user's access to an app. Session-authed. */
@@ -118,7 +142,43 @@ declare class AccountsAdmin {
118
142
  /** Members of a user_group (the workspace's agents). Service-key authed (`stats:read`). */
119
143
  listGroupMembers(groupId: string, opts?: {
120
144
  appId?: string;
145
+ search?: string;
146
+ limit?: number;
147
+ offset?: number;
121
148
  }): Promise<GroupMember[]>;
149
+ /**
150
+ * A PAGE of a group's members, plus the total before paging — what a paginated, searchable user
151
+ * list needs. `listGroupMembers` stays the "give me everyone" convenience for the agent picker.
152
+ *
153
+ * Search and paging are applied by Accounts in SQL: fetching every member to filter in the browser
154
+ * does not scale, and leaves the pager unable to know how many pages exist. Omit `limit` and the
155
+ * full list comes back (with `totalCount` equal to its length).
156
+ */
157
+ listGroupMembersPage(groupId: string, opts?: {
158
+ appId?: string;
159
+ search?: string;
160
+ limit?: number;
161
+ offset?: number;
162
+ }): Promise<{
163
+ members: GroupMember[];
164
+ totalCount: number;
165
+ }>;
166
+ /**
167
+ * The app's PENDING invites — people invited but not yet accepted. Session-authed.
168
+ *
169
+ * An invitee is not a membership, so they appear in no member list; without this a successful
170
+ * invite looks like nothing happened until the person accepts. Pass `userGroupId` to scope to one
171
+ * workspace. Accounts excludes consumed and expired invites.
172
+ */
173
+ listAppInvites(appSlug: string, opts?: {
174
+ userGroupId?: string;
175
+ search?: string;
176
+ limit?: number;
177
+ offset?: number;
178
+ }): Promise<{
179
+ invites: PendingInvite[];
180
+ totalCount: number;
181
+ }>;
122
182
  /** Read a user's app-scoped `profileData` (the store for per-app preferences). Session-authed. */
123
183
  getAppProfile(userId: string): Promise<Record<string, string>>;
124
184
  /** Write app-scoped `profileData` fields for a user (e.g. availability, soundEnabled). All values
@@ -143,4 +203,4 @@ declare class AccountsAdmin {
143
203
  /** Convenience factory mirroring the SDK's other `create*` helpers. */
144
204
  declare function createAccountsAdmin(opts: AccountsAdminOptions): AccountsAdmin;
145
205
 
146
- export { AccountsAdmin, AccountsAdminError, type AccountsAdminOptions, type AccountsUser, type AppMember, type ChangePasswordResult, type GroupMember, type InviteResult, type ListUsersQuery, type ListUsersResult, createAccountsAdmin };
206
+ export { AccountsAdmin, AccountsAdminError, type AccountsAdminOptions, type AccountsUser, type AppMember, type ChangePasswordResult, type GroupMember, type InviteResult, type ListUsersQuery, type ListUsersResult, type PendingInvite, createAccountsAdmin };
package/dist/admin.js CHANGED
@@ -64,14 +64,29 @@ var AccountsAdmin = class {
64
64
  });
65
65
  }
66
66
  // ── App membership (invite / list / remove) ─────────────────────────────────
67
- /** Invite an email to an app: adds an existing Accounts user immediately, else emails an accept
68
- * link. Session-authed (caller must have access to the app). */
67
+ /**
68
+ * Invite an email to an app, emailing them an accept link. Session-authed (the caller must have
69
+ * access to the app).
70
+ *
71
+ * Pass `userGroupId` to say which WORKSPACE the invitee joins when they accept. Omit it and they
72
+ * join the app but no workspace — Accounts then treats them as a brand-new user on first login and
73
+ * sends them to create their own, instead of landing in the workspace that invited them. Accounts
74
+ * validates the group against the CALLER's own memberships, so this cannot target a foreign group.
75
+ */
69
76
  async inviteToApp(appSlug, input) {
70
77
  return this.request("POST", `/api/auth/apps/${encodeURIComponent(appSlug)}/invites`, {
71
78
  auth: "session",
72
79
  body: input
73
80
  });
74
81
  }
82
+ /** Revoke a pending invite (withdraw one sent in error). Session-authed. */
83
+ async revokeAppInvite(appSlug, inviteId) {
84
+ await this.request(
85
+ "DELETE",
86
+ `/api/auth/apps/${encodeURIComponent(appSlug)}/invites?inviteId=${encodeURIComponent(inviteId)}`,
87
+ { auth: "session" }
88
+ );
89
+ }
75
90
  /** Everyone with access to an app. Session-authed. */
76
91
  async listAppUsers(appSlug) {
77
92
  const data = await this.request(
@@ -92,13 +107,53 @@ var AccountsAdmin = class {
92
107
  // ── Groups (agent picker) ───────────────────────────────────────────────────
93
108
  /** Members of a user_group (the workspace's agents). Service-key authed (`stats:read`). */
94
109
  async listGroupMembers(groupId, opts = {}) {
95
- const suffix = opts.appId ? `?appId=${encodeURIComponent(opts.appId)}` : "";
110
+ const { members } = await this.listGroupMembersPage(groupId, opts);
111
+ return members;
112
+ }
113
+ /**
114
+ * A PAGE of a group's members, plus the total before paging — what a paginated, searchable user
115
+ * list needs. `listGroupMembers` stays the "give me everyone" convenience for the agent picker.
116
+ *
117
+ * Search and paging are applied by Accounts in SQL: fetching every member to filter in the browser
118
+ * does not scale, and leaves the pager unable to know how many pages exist. Omit `limit` and the
119
+ * full list comes back (with `totalCount` equal to its length).
120
+ */
121
+ async listGroupMembersPage(groupId, opts = {}) {
122
+ const qs = new URLSearchParams();
123
+ if (opts.appId) qs.set("appId", opts.appId);
124
+ if (opts.search) qs.set("search", opts.search);
125
+ if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
126
+ if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
127
+ const suffix = qs.size ? `?${qs}` : "";
96
128
  const data = await this.request(
97
129
  "GET",
98
130
  `/api/auth/user-groups/${encodeURIComponent(groupId)}/members${suffix}`,
99
131
  { auth: "key" }
100
132
  );
101
- return data.members ?? [];
133
+ const members = data.members ?? [];
134
+ return { members, totalCount: data.totalCount ?? members.length };
135
+ }
136
+ /**
137
+ * The app's PENDING invites — people invited but not yet accepted. Session-authed.
138
+ *
139
+ * An invitee is not a membership, so they appear in no member list; without this a successful
140
+ * invite looks like nothing happened until the person accepts. Pass `userGroupId` to scope to one
141
+ * workspace. Accounts excludes consumed and expired invites.
142
+ */
143
+ async listAppInvites(appSlug, opts = {}) {
144
+ const qs = new URLSearchParams();
145
+ if (opts.userGroupId) qs.set("userGroupId", opts.userGroupId);
146
+ if (opts.search) qs.set("search", opts.search);
147
+ if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
148
+ if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
149
+ const suffix = qs.size ? `?${qs}` : "";
150
+ const data = await this.request(
151
+ "GET",
152
+ `/api/auth/apps/${encodeURIComponent(appSlug)}/invites${suffix}`,
153
+ { auth: "session" }
154
+ );
155
+ const invites = data.invites ?? [];
156
+ return { invites, totalCount: data.totalCount ?? invites.length };
102
157
  }
103
158
  // ── App-scoped profile (availability / sound / any per-app preference) ───────
104
159
  /** Read a user's app-scoped `profileData` (the store for per-app preferences). Session-authed. */
package/package.json CHANGED
@@ -1,73 +1,73 @@
1
1
  {
2
- "name": "@~lyre/auth",
3
- "version": "0.3.0",
4
- "description": "Shared Axis Accounts auth SDK — framework-agnostic session/identity core (HMAC-signed cookies, accounts login/callback/logout) plus an optional turnkey SvelteKit adapter.",
5
- "type": "module",
6
- "sideEffects": false,
7
- "license": "MIT",
8
- "author": "Britam Trust Services / Lyre",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/kigathi-chege/lyre-auth-sdk.git"
12
- },
13
- "homepage": "https://github.com/kigathi-chege/lyre-auth-sdk#readme",
14
- "bugs": {
15
- "url": "https://github.com/kigathi-chege/lyre-auth-sdk/issues"
16
- },
17
- "keywords": [
18
- "auth",
19
- "sso",
20
- "accounts",
21
- "session",
22
- "sveltekit",
23
- "hmac",
24
- "lyre"
25
- ],
26
- "main": "./dist/index.js",
27
- "types": "./dist/index.d.ts",
28
- "exports": {
29
- ".": {
30
- "types": "./dist/index.d.ts",
31
- "import": "./dist/index.js"
32
- },
33
- "./sveltekit": {
34
- "types": "./dist/sveltekit.d.ts",
35
- "import": "./dist/sveltekit.js"
36
- },
37
- "./nest": {
38
- "types": "./dist/nest.d.ts",
39
- "import": "./dist/nest.js"
40
- },
41
- "./admin": {
42
- "types": "./dist/admin.d.ts",
43
- "import": "./dist/admin.js"
44
- },
45
- "./package.json": "./package.json"
46
- },
47
- "files": [
48
- "dist",
49
- "README.md",
50
- "LICENSE"
51
- ],
52
- "scripts": {
53
- "build": "tsup src/index.ts src/sveltekit.ts src/nest.ts src/admin.ts --format esm --dts --clean",
54
- "check": "tsc --noEmit"
55
- },
56
- "peerDependencies": {
57
- "@sveltejs/kit": "^2.0.0"
58
- },
59
- "peerDependenciesMeta": {
60
- "@sveltejs/kit": {
61
- "optional": true
62
- }
63
- },
64
- "devDependencies": {
65
- "@sveltejs/kit": "^2.0.0",
66
- "@types/node": "^22",
67
- "tsup": "^8.3.5",
68
- "typescript": "^6.0.2"
69
- },
70
- "publishConfig": {
71
- "access": "public"
72
- }
2
+ "name": "@~lyre/auth",
3
+ "version": "0.4.0",
4
+ "description": "Shared Axis Accounts auth SDK \u2014 framework-agnostic session/identity core (HMAC-signed cookies, accounts login/callback/logout) plus an optional turnkey SvelteKit adapter.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "license": "MIT",
8
+ "author": "Britam Trust Services / Lyre",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/kigathi-chege/lyre-auth-sdk.git"
12
+ },
13
+ "homepage": "https://github.com/kigathi-chege/lyre-auth-sdk#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/kigathi-chege/lyre-auth-sdk/issues"
16
+ },
17
+ "keywords": [
18
+ "auth",
19
+ "sso",
20
+ "accounts",
21
+ "session",
22
+ "sveltekit",
23
+ "hmac",
24
+ "lyre"
25
+ ],
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./sveltekit": {
34
+ "types": "./dist/sveltekit.d.ts",
35
+ "import": "./dist/sveltekit.js"
36
+ },
37
+ "./nest": {
38
+ "types": "./dist/nest.d.ts",
39
+ "import": "./dist/nest.js"
40
+ },
41
+ "./admin": {
42
+ "types": "./dist/admin.d.ts",
43
+ "import": "./dist/admin.js"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "scripts": {
53
+ "build": "tsup src/index.ts src/sveltekit.ts src/nest.ts src/admin.ts --format esm --dts --clean",
54
+ "check": "tsc --noEmit"
55
+ },
56
+ "peerDependencies": {
57
+ "@sveltejs/kit": "^2.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@sveltejs/kit": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "devDependencies": {
65
+ "@sveltejs/kit": "^2.0.0",
66
+ "@types/node": "^22",
67
+ "tsup": "^8.3.5",
68
+ "typescript": "^6.0.2"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ }
73
73
  }