@stackframe/stack-shared 2.8.2 → 2.8.5

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/config/format.d.ts +38 -0
  3. package/dist/config/format.js +224 -0
  4. package/dist/config/schema.d.ts +721 -0
  5. package/dist/config/schema.js +185 -0
  6. package/dist/crud.js +1 -1
  7. package/dist/interface/adminInterface.d.ts +7 -7
  8. package/dist/interface/adminInterface.js +7 -7
  9. package/dist/interface/clientInterface.d.ts +46 -4
  10. package/dist/interface/clientInterface.js +66 -2
  11. package/dist/interface/crud/current-user.d.ts +2 -2
  12. package/dist/interface/crud/{api-keys.d.ts → internal-api-keys.d.ts} +7 -7
  13. package/dist/interface/crud/{api-keys.js → internal-api-keys.js} +10 -10
  14. package/dist/interface/crud/project-api-keys.d.ts +188 -0
  15. package/dist/interface/crud/project-api-keys.js +76 -0
  16. package/dist/interface/crud/projects.d.ts +53 -20
  17. package/dist/interface/crud/projects.js +15 -5
  18. package/dist/interface/crud/team-member-profiles.d.ts +4 -4
  19. package/dist/interface/crud/users.d.ts +8 -8
  20. package/dist/interface/serverInterface.d.ts +2 -1
  21. package/dist/interface/serverInterface.js +4 -0
  22. package/dist/interface/webhooks.d.ts +2 -2
  23. package/dist/known-errors.d.ts +35 -1
  24. package/dist/known-errors.js +42 -4
  25. package/dist/schema-fields.d.ts +6 -5
  26. package/dist/schema-fields.js +36 -3
  27. package/dist/utils/api-keys.d.ts +23 -0
  28. package/dist/utils/api-keys.js +76 -0
  29. package/dist/utils/bytes.d.ts +3 -0
  30. package/dist/utils/bytes.js +55 -6
  31. package/dist/utils/caches.d.ts +10 -10
  32. package/dist/utils/hashes.d.ts +1 -1
  33. package/dist/utils/hashes.js +1 -3
  34. package/dist/utils/objects.d.ts +35 -2
  35. package/dist/utils/objects.js +128 -0
  36. package/dist/utils/promises.d.ts +1 -1
  37. package/dist/utils/promises.js +9 -10
  38. package/dist/utils/stores.d.ts +6 -6
  39. package/dist/utils/types.d.ts +17 -0
  40. package/package.json +2 -1
@@ -0,0 +1,185 @@
1
+ import * as schemaFields from "../schema-fields";
2
+ import { yupBoolean, yupObject, yupRecord, yupString } from "../schema-fields";
3
+ import { allProviders } from "../utils/oauth";
4
+ import { get, has, isObjectLike, set } from "../utils/objects";
5
+ // NOTE: The validation schemas in here are all schematic validators, not sanity-check validators.
6
+ // For more info, see ./README.md
7
+ export const configLevels = ['project', 'branch', 'environment', 'organization'];
8
+ const permissionRegex = /^\$?[a-z0-9_:]+$/;
9
+ /**
10
+ * All fields that can be overridden at this level.
11
+ */
12
+ export const projectConfigSchema = yupObject({});
13
+ // --- NEW RBAC Schema ---
14
+ const branchRbacDefaultPermissions = yupRecord(yupString().optional().matches(permissionRegex), yupBoolean().isTrue().optional()).optional();
15
+ const branchRbacSchema = yupObject({
16
+ permissions: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
17
+ description: yupString().optional(),
18
+ scope: yupString().oneOf(['team', 'project']).optional(),
19
+ containedPermissionIds: yupRecord(yupString().optional().matches(permissionRegex), yupBoolean().isTrue().optional()).optional(),
20
+ }).optional()).optional(),
21
+ defaultPermissions: yupObject({
22
+ teamCreator: branchRbacDefaultPermissions,
23
+ teamMember: branchRbacDefaultPermissions,
24
+ signUp: branchRbacDefaultPermissions,
25
+ }).optional(),
26
+ }).optional();
27
+ // --- END NEW RBAC Schema ---
28
+ // --- NEW API Keys Schema ---
29
+ const branchApiKeysSchema = yupObject({
30
+ enabled: yupObject({
31
+ team: yupBoolean().optional(),
32
+ user: yupBoolean().optional(),
33
+ }).optional(),
34
+ }).optional();
35
+ // --- END NEW API Keys Schema ---
36
+ const branchAuthSchema = yupObject({
37
+ allowSignUp: yupBoolean().optional(),
38
+ password: yupObject({
39
+ allowSignIn: yupBoolean().optional(),
40
+ }).optional(),
41
+ otp: yupObject({
42
+ allowSignIn: yupBoolean().optional(),
43
+ }).optional(),
44
+ passkey: yupObject({
45
+ allowSignIn: yupBoolean().optional(),
46
+ }).optional(),
47
+ oauth: yupObject({
48
+ accountMergeStrategy: yupString().oneOf(['link_method', 'raise_error', 'allow_duplicates']).optional(),
49
+ providers: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
50
+ type: yupString().oneOf(allProviders).optional(),
51
+ allowSignIn: yupBoolean().optional(),
52
+ allowConnectedAccounts: yupBoolean().optional(),
53
+ }).defined()).optional(),
54
+ }).optional(),
55
+ }).optional();
56
+ const branchDomain = yupObject({
57
+ allowLocalhost: yupBoolean().optional(),
58
+ }).optional();
59
+ export const branchConfigSchema = projectConfigSchema.concat(yupObject({
60
+ rbac: branchRbacSchema,
61
+ teams: yupObject({
62
+ createPersonalTeamOnSignUp: yupBoolean().optional(),
63
+ allowClientTeamCreation: yupBoolean().optional(),
64
+ }).optional(),
65
+ users: yupObject({
66
+ allowClientUserDeletion: yupBoolean().optional(),
67
+ }).optional(),
68
+ apiKeys: branchApiKeysSchema,
69
+ domains: branchDomain,
70
+ auth: branchAuthSchema,
71
+ emails: yupObject({}),
72
+ }));
73
+ export const environmentConfigSchema = branchConfigSchema.concat(yupObject({
74
+ auth: branchConfigSchema.getNested("auth").concat(yupObject({
75
+ oauth: branchConfigSchema.getNested("auth").getNested("oauth").concat(yupObject({
76
+ providers: yupRecord(yupString().optional().matches(permissionRegex), yupObject({
77
+ type: yupString().oneOf(allProviders).optional(),
78
+ isShared: yupBoolean().optional(),
79
+ clientId: schemaFields.oauthClientIdSchema.optional(),
80
+ clientSecret: schemaFields.oauthClientSecretSchema.optional(),
81
+ facebookConfigId: schemaFields.oauthFacebookConfigIdSchema.optional(),
82
+ microsoftTenantId: schemaFields.oauthMicrosoftTenantIdSchema.optional(),
83
+ allowSignIn: yupBoolean().optional(),
84
+ allowConnectedAccounts: yupBoolean().optional(),
85
+ })).optional(),
86
+ }).optional()),
87
+ })),
88
+ emails: branchConfigSchema.getNested("emails").concat(yupObject({
89
+ server: yupObject({
90
+ isShared: yupBoolean().optional(),
91
+ host: schemaFields.emailHostSchema.optional().nonEmpty(),
92
+ port: schemaFields.emailPortSchema.optional(),
93
+ username: schemaFields.emailUsernameSchema.optional().nonEmpty(),
94
+ password: schemaFields.emailPasswordSchema.optional().nonEmpty(),
95
+ senderName: schemaFields.emailSenderNameSchema.optional().nonEmpty(),
96
+ senderEmail: schemaFields.emailSenderEmailSchema.optional().nonEmpty(),
97
+ }),
98
+ }).optional()),
99
+ domains: branchConfigSchema.getNested("domains").concat(yupObject({
100
+ trustedDomains: yupRecord(yupString().uuid().optional(), yupObject({
101
+ baseUrl: schemaFields.urlSchema.optional(),
102
+ handlerPath: schemaFields.handlerPathSchema.optional(),
103
+ })).optional(),
104
+ })),
105
+ }));
106
+ export const organizationConfigSchema = environmentConfigSchema.concat(yupObject({}));
107
+ // Defaults
108
+ // these are objects that are merged together to form the rendered config (see ./README.md)
109
+ // Wherever an object could be used as a value, a function can instead be used to generate the default values on a per-key basis
110
+ export const projectConfigDefaults = {};
111
+ export const branchConfigDefaults = {};
112
+ export const environmentConfigDefaults = {};
113
+ export const organizationConfigDefaults = {
114
+ rbac: {
115
+ permissions: (key) => ({}),
116
+ defaultPermissions: {
117
+ teamCreator: {},
118
+ teamMember: {},
119
+ signUp: {},
120
+ },
121
+ },
122
+ apiKeys: {
123
+ enabled: {
124
+ team: false,
125
+ user: false,
126
+ },
127
+ },
128
+ teams: {
129
+ createPersonalTeamOnSignUp: false,
130
+ allowClientTeamCreation: false,
131
+ },
132
+ users: {
133
+ allowClientUserDeletion: false,
134
+ },
135
+ domains: {
136
+ allowLocalhost: false,
137
+ trustedDomains: (key) => ({
138
+ handlerPath: '/handler',
139
+ }),
140
+ },
141
+ auth: {
142
+ allowSignUp: true,
143
+ password: {
144
+ allowSignIn: false,
145
+ },
146
+ otp: {
147
+ allowSignIn: false,
148
+ },
149
+ passkey: {
150
+ allowSignIn: false,
151
+ },
152
+ oauth: {
153
+ accountMergeStrategy: 'link_method',
154
+ providers: (key) => ({
155
+ allowSignIn: false,
156
+ allowConnectedAccounts: false,
157
+ }),
158
+ },
159
+ },
160
+ emails: {
161
+ server: {
162
+ isShared: true,
163
+ },
164
+ },
165
+ };
166
+ export function applyDefaults(defaults, config) {
167
+ const res = { ...typeof defaults === 'function' ? {} : defaults };
168
+ for (const [key, mergeValue] of Object.entries(config)) {
169
+ const baseValue = typeof defaults === 'function' ? defaults(key) : (has(defaults, key) ? get(defaults, key) : undefined);
170
+ if (baseValue !== undefined) {
171
+ if (isObjectLike(baseValue) && isObjectLike(mergeValue)) {
172
+ set(res, key, applyDefaults(baseValue, mergeValue));
173
+ continue;
174
+ }
175
+ }
176
+ set(res, key, mergeValue);
177
+ }
178
+ return res;
179
+ }
180
+ import.meta.vitest?.test("applyDefaults", ({ expect }) => {
181
+ expect(applyDefaults({ a: 1 }, { a: 2 })).toEqual({ a: 2 });
182
+ expect(applyDefaults({ a: { b: 1 } }, { a: { c: 2 } })).toEqual({ a: { b: 1, c: 2 } });
183
+ expect(applyDefaults((key) => ({ b: key }), { a: {} })).toEqual({ a: { b: "a" } });
184
+ expect(applyDefaults({ a: (key) => ({ b: key }) }, { a: { c: { d: 1 } } })).toEqual({ a: { c: { b: "c", d: 1 } } });
185
+ });
package/dist/crud.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { yupObject, yupString } from './schema-fields';
1
2
  import { filterUndefined } from './utils/objects';
2
3
  export function createCrud(options) {
3
4
  const docs = options.docs ?? {};
@@ -52,7 +53,6 @@ export function createCrud(options) {
52
53
  hasDelete: !!admin.deleteSchema,
53
54
  };
54
55
  }
55
- import { yupObject, yupString } from './schema-fields';
56
56
  import.meta.vitest?.test("createCrud", ({ expect }) => {
57
57
  // Test with empty options
58
58
  const emptyCrud = createCrud({});
@@ -1,7 +1,7 @@
1
1
  import { InternalSession } from "../sessions";
2
- import { ApiKeysCrud } from "./crud/api-keys";
3
2
  import { EmailTemplateCrud, EmailTemplateType } from "./crud/email-templates";
4
3
  import { InternalEmailsCrud } from "./crud/emails";
4
+ import { InternalApiKeysCrud } from "./crud/internal-api-keys";
5
5
  import { ProjectPermissionDefinitionsCrud } from "./crud/project-permissions";
6
6
  import { ProjectsCrud } from "./crud/projects";
7
7
  import { SvixTokenCrud } from "./crud/svix-token";
@@ -12,14 +12,14 @@ export type AdminAuthApplicationOptions = ServerAuthApplicationOptions & ({
12
12
  } | {
13
13
  projectOwnerSession: InternalSession;
14
14
  });
15
- export type ApiKeyCreateCrudRequest = {
15
+ export type InternalApiKeyCreateCrudRequest = {
16
16
  has_publishable_client_key: boolean;
17
17
  has_secret_server_key: boolean;
18
18
  has_super_secret_admin_key: boolean;
19
19
  expires_at_millis: number;
20
20
  description: string;
21
21
  };
22
- export type ApiKeyCreateCrudResponse = ApiKeysCrud["Admin"]["Read"] & {
22
+ export type InternalApiKeyCreateCrudResponse = InternalApiKeysCrud["Admin"]["Read"] & {
23
23
  publishable_client_key?: string;
24
24
  secret_server_key?: string;
25
25
  super_secret_admin_key?: string;
@@ -35,10 +35,10 @@ export declare class StackAdminInterface extends StackServerInterface {
35
35
  }>;
36
36
  getProject(): Promise<ProjectsCrud["Admin"]["Read"]>;
37
37
  updateProject(update: ProjectsCrud["Admin"]["Update"]): Promise<ProjectsCrud["Admin"]["Read"]>;
38
- createApiKey(options: ApiKeyCreateCrudRequest): Promise<ApiKeyCreateCrudResponse>;
39
- listApiKeys(): Promise<ApiKeysCrud["Admin"]["Read"][]>;
40
- revokeApiKeyById(id: string): Promise<void>;
41
- getApiKey(id: string, session: InternalSession): Promise<ApiKeysCrud["Admin"]["Read"]>;
38
+ createInternalApiKey(options: InternalApiKeyCreateCrudRequest): Promise<InternalApiKeyCreateCrudResponse>;
39
+ listInternalApiKeys(): Promise<InternalApiKeysCrud["Admin"]["Read"][]>;
40
+ revokeInternalApiKeyById(id: string): Promise<void>;
41
+ getInternalApiKey(id: string, session: InternalSession): Promise<InternalApiKeysCrud["Admin"]["Read"]>;
42
42
  listEmailTemplates(): Promise<EmailTemplateCrud['Admin']['Read'][]>;
43
43
  updateEmailTemplate(type: EmailTemplateType, data: EmailTemplateCrud['Admin']['Update']): Promise<EmailTemplateCrud['Admin']['Read']>;
44
44
  resetEmailTemplate(type: EmailTemplateType): Promise<void>;
@@ -14,13 +14,13 @@ export class StackAdminInterface extends StackServerInterface {
14
14
  }, session, requestType);
15
15
  }
16
16
  async getProject() {
17
- const response = await this.sendAdminRequest("/projects/current", {
17
+ const response = await this.sendAdminRequest("/internal/projects/current", {
18
18
  method: "GET",
19
19
  }, null);
20
20
  return await response.json();
21
21
  }
22
22
  async updateProject(update) {
23
- const response = await this.sendAdminRequest("/projects/current", {
23
+ const response = await this.sendAdminRequest("/internal/projects/current", {
24
24
  method: "PATCH",
25
25
  headers: {
26
26
  "content-type": "application/json",
@@ -29,7 +29,7 @@ export class StackAdminInterface extends StackServerInterface {
29
29
  }, null);
30
30
  return await response.json();
31
31
  }
32
- async createApiKey(options) {
32
+ async createInternalApiKey(options) {
33
33
  const response = await this.sendAdminRequest("/internal/api-keys", {
34
34
  method: "POST",
35
35
  headers: {
@@ -39,12 +39,12 @@ export class StackAdminInterface extends StackServerInterface {
39
39
  }, null);
40
40
  return await response.json();
41
41
  }
42
- async listApiKeys() {
42
+ async listInternalApiKeys() {
43
43
  const response = await this.sendAdminRequest("/internal/api-keys", {}, null);
44
44
  const result = await response.json();
45
45
  return result.items;
46
46
  }
47
- async revokeApiKeyById(id) {
47
+ async revokeInternalApiKeyById(id) {
48
48
  await this.sendAdminRequest(`/internal/api-keys/${id}`, {
49
49
  method: "PATCH",
50
50
  headers: {
@@ -55,7 +55,7 @@ export class StackAdminInterface extends StackServerInterface {
55
55
  }),
56
56
  }, null);
57
57
  }
58
- async getApiKey(id, session) {
58
+ async getInternalApiKey(id, session) {
59
59
  const response = await this.sendAdminRequest(`/internal/api-keys/${id}`, {}, session);
60
60
  return await response.json();
61
61
  }
@@ -145,7 +145,7 @@ export class StackAdminInterface extends StackServerInterface {
145
145
  return await response.json();
146
146
  }
147
147
  async deleteProject() {
148
- await this.sendAdminRequest("/projects/current", {
148
+ await this.sendAdminRequest("/internal/projects/current", {
149
149
  method: "DELETE",
150
150
  }, null);
151
151
  }
@@ -1,3 +1,4 @@
1
+ import * as yup from 'yup';
1
2
  import { KnownErrors } from '../known-errors';
2
3
  import { AccessToken, InternalSession, RefreshToken } from '../sessions';
3
4
  import { ReadonlyJson } from '../utils/json';
@@ -6,8 +7,9 @@ import { Result } from "../utils/results";
6
7
  import { ContactChannelsCrud } from './crud/contact-channels';
7
8
  import { CurrentUserCrud } from './crud/current-user';
8
9
  import { ConnectedAccountAccessTokenCrud } from './crud/oauth';
10
+ import { TeamApiKeysCrud, UserApiKeysCrud, teamApiKeysCreateInputSchema, teamApiKeysCreateOutputSchema, userApiKeysCreateInputSchema, userApiKeysCreateOutputSchema } from './crud/project-api-keys';
9
11
  import { ProjectPermissionsCrud } from './crud/project-permissions';
10
- import { InternalProjectsCrud, ProjectsCrud } from './crud/projects';
12
+ import { AdminUserProjectsCrud, ClientProjectsCrud } from './crud/projects';
11
13
  import { SessionsCrud } from './crud/sessions';
12
14
  import { TeamInvitationCrud } from './crud/team-invitation';
13
15
  import { TeamMemberProfilesCrud } from './crud/team-member-profiles';
@@ -192,10 +194,10 @@ export declare class StackClientInterface {
192
194
  recursive: boolean;
193
195
  }, session: InternalSession): Promise<ProjectPermissionsCrud['Client']['Read'][]>;
194
196
  listCurrentUserTeams(session: InternalSession): Promise<TeamsCrud["Client"]["Read"][]>;
195
- getClientProject(): Promise<Result<ProjectsCrud['Client']['Read'], KnownErrors["ProjectNotFound"]>>;
197
+ getClientProject(): Promise<Result<ClientProjectsCrud['Client']['Read'], KnownErrors["ProjectNotFound"]>>;
196
198
  updateClientUser(update: CurrentUserCrud["Client"]["Update"], session: InternalSession): Promise<void>;
197
- listProjects(session: InternalSession): Promise<InternalProjectsCrud['Client']['Read'][]>;
198
- createProject(project: InternalProjectsCrud['Client']['Create'], session: InternalSession): Promise<InternalProjectsCrud['Client']['Read']>;
199
+ listProjects(session: InternalSession): Promise<AdminUserProjectsCrud['Client']['Read'][]>;
200
+ createProject(project: AdminUserProjectsCrud['Client']['Create'], session: InternalSession): Promise<AdminUserProjectsCrud['Client']['Read']>;
199
201
  createProviderAccessToken(provider: string, scope: string, session: InternalSession): Promise<ConnectedAccountAccessTokenCrud['Client']['Read']>;
200
202
  createClientTeam(data: TeamsCrud['Client']['Create'], session: InternalSession): Promise<TeamsCrud['Client']['Read']>;
201
203
  deleteTeam(teamId: string, session: InternalSession): Promise<void>;
@@ -208,4 +210,44 @@ export declare class StackClientInterface {
208
210
  listClientContactChannels(session: InternalSession): Promise<ContactChannelsCrud['Client']['Read'][]>;
209
211
  sendCurrentUserContactChannelVerificationEmail(contactChannelId: string, callbackUrl: string, session: InternalSession): Promise<Result<undefined, KnownErrors["EmailAlreadyVerified"]>>;
210
212
  cliLogin(loginCode: string, refreshToken: string, session: InternalSession): Promise<Result<undefined, KnownErrors["SchemaError"]>>;
213
+ private _getApiKeyRequestInfo;
214
+ listProjectApiKeys(options: {
215
+ user_id: string;
216
+ }, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'][]>;
217
+ listProjectApiKeys(options: {
218
+ team_id: string;
219
+ }, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read'][]>;
220
+ listProjectApiKeys(options: {
221
+ user_id: string;
222
+ } | {
223
+ team_id: string;
224
+ }, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<(UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read'])[]>;
225
+ createProjectApiKey(data: yup.InferType<typeof userApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof userApiKeysCreateOutputSchema>>;
226
+ createProjectApiKey(data: yup.InferType<typeof teamApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof teamApiKeysCreateOutputSchema>>;
227
+ createProjectApiKey(data: yup.InferType<typeof userApiKeysCreateInputSchema> | yup.InferType<typeof teamApiKeysCreateInputSchema>, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<yup.InferType<typeof userApiKeysCreateOutputSchema> | yup.InferType<typeof teamApiKeysCreateOutputSchema>>;
228
+ getProjectApiKey(options: {
229
+ user_id: string | null;
230
+ }, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read']>;
231
+ getProjectApiKey(options: {
232
+ team_id: string;
233
+ }, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read']>;
234
+ getProjectApiKey(options: {
235
+ user_id: string | null;
236
+ } | {
237
+ team_id: string;
238
+ }, keyId: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read']>;
239
+ updateProjectApiKey(options: {
240
+ user_id: string;
241
+ }, keyId: string, data: UserApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read']>;
242
+ updateProjectApiKey(options: {
243
+ team_id: string;
244
+ }, keyId: string, data: TeamApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read']>;
245
+ updateProjectApiKey(options: {
246
+ user_id: string;
247
+ } | {
248
+ team_id: string;
249
+ }, keyId: string, data: UserApiKeysCrud['Client']['Update'] | TeamApiKeysCrud['Client']['Update'], session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read']>;
250
+ checkProjectApiKey(type: "user", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | null>;
251
+ checkProjectApiKey(type: "team", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<TeamApiKeysCrud['Client']['Read'] | null>;
252
+ checkProjectApiKey(type: "user" | "team", apiKey: string, session: InternalSession | null, requestType: "client" | "server" | "admin"): Promise<UserApiKeysCrud['Client']['Read'] | TeamApiKeysCrud['Client']['Read'] | null>;
211
253
  }
@@ -5,7 +5,7 @@ import { generateSecureRandomString } from '../utils/crypto';
5
5
  import { StackAssertionError, throwErr } from '../utils/errors';
6
6
  import { globalVar } from '../utils/globals';
7
7
  import { HTTP_METHODS } from '../utils/http';
8
- import { filterUndefined } from '../utils/objects';
8
+ import { filterUndefined, filterUndefinedOrNull } from '../utils/objects';
9
9
  import { wait } from '../utils/promises';
10
10
  import { Result } from "../utils/results";
11
11
  import { deindent } from '../utils/strings';
@@ -173,7 +173,7 @@ export class StackClientInterface {
173
173
  }
174
174
  const params = {
175
175
  /**
176
- * This fetch may be cross-origin, in which case we don't want to send cookies of the
176
+ * This fetch may be cross-origin, in which case we don't want to send cookies of the
177
177
  * original origin (this is the default behavior of `credentials`).
178
178
  *
179
179
  * To help debugging, also omit cookies on same-origin, so we don't accidentally
@@ -942,4 +942,68 @@ export class StackClientInterface {
942
942
  }
943
943
  return Result.ok(undefined);
944
944
  }
945
+ async _getApiKeyRequestInfo(options) {
946
+ if ("user_id" in options && "team_id" in options) {
947
+ throw new StackAssertionError("Cannot specify both user_id and team_id in _getApiKeyRequestInfo");
948
+ }
949
+ return {
950
+ endpoint: "team_id" in options ? "/team-api-keys" : "/user-api-keys",
951
+ queryParams: new URLSearchParams(filterUndefinedOrNull(options)),
952
+ };
953
+ }
954
+ async listProjectApiKeys(options, session, requestType) {
955
+ const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
956
+ const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
957
+ const response = await sendRequest(`${endpoint}?${queryParams.toString()}`, {
958
+ method: "GET",
959
+ }, session, requestType);
960
+ const json = await response.json();
961
+ return json.items;
962
+ }
963
+ async createProjectApiKey(data, session, requestType) {
964
+ const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
965
+ const { endpoint } = await this._getApiKeyRequestInfo(data);
966
+ const response = await sendRequest(`${endpoint}`, {
967
+ method: "POST",
968
+ headers: {
969
+ "content-type": "application/json",
970
+ },
971
+ body: JSON.stringify(data),
972
+ }, session, requestType);
973
+ return await response.json();
974
+ }
975
+ async getProjectApiKey(options, keyId, session, requestType) {
976
+ const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
977
+ const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
978
+ const response = await sendRequest(`${endpoint}/${keyId}?${queryParams.toString()}`, {
979
+ method: "GET",
980
+ }, session, requestType);
981
+ return await response.json();
982
+ }
983
+ async updateProjectApiKey(options, keyId, data, session, requestType) {
984
+ const sendRequest = (requestType === "client" ? this.sendClientRequest : this.sendServerRequest).bind(this);
985
+ const { endpoint, queryParams } = await this._getApiKeyRequestInfo(options);
986
+ const response = await sendRequest(`${endpoint}/${keyId}?${queryParams.toString()}`, {
987
+ method: "PATCH",
988
+ headers: {
989
+ "content-type": "application/json",
990
+ },
991
+ body: JSON.stringify(data),
992
+ }, session, requestType);
993
+ return await response.json();
994
+ }
995
+ async checkProjectApiKey(type, apiKey, session, requestType) {
996
+ const sendRequest = (requestType === "client" ? this.sendClientRequestAndCatchKnownError : this.sendServerRequestAndCatchKnownError).bind(this);
997
+ const result = await sendRequest(`/${type}-api-keys/check`, {
998
+ method: "POST",
999
+ headers: {
1000
+ "content-type": "application/json",
1001
+ },
1002
+ body: JSON.stringify({ api_key: apiKey }),
1003
+ }, session, [KnownErrors.ApiKeyNotValid]);
1004
+ if (result.status === "error") {
1005
+ return null;
1006
+ }
1007
+ return await result.data.json();
1008
+ }
945
1009
  }
@@ -1,7 +1,6 @@
1
1
  import { CrudTypeOf } from "../../crud";
2
2
  export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions<{
3
3
  clientReadSchema: import("yup").ObjectSchema<{
4
- primary_email: string | null;
5
4
  id: string;
6
5
  display_name: string | null;
7
6
  oauth_providers: {
@@ -12,6 +11,7 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
12
11
  profile_image_url: string | null;
13
12
  client_metadata: {} | null;
14
13
  client_read_only_metadata: {} | null;
14
+ primary_email: string | null;
15
15
  primary_email_verified: boolean;
16
16
  passkey_auth_enabled: boolean;
17
17
  otp_auth_enabled: boolean;
@@ -68,8 +68,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
68
68
  client_read_only_metadata?: {} | null | undefined;
69
69
  server_metadata?: {} | null | undefined;
70
70
  id: string;
71
- created_at_millis: number;
72
71
  display_name: string;
72
+ created_at_millis: number;
73
73
  profile_image_url: string | null;
74
74
  } | null;
75
75
  selected_team_id: string | null;
@@ -1,5 +1,5 @@
1
1
  import { CrudTypeOf } from "../../crud";
2
- export declare const apiKeysCreateInputSchema: import("yup").ObjectSchema<{
2
+ export declare const internalApiKeysCreateInputSchema: import("yup").ObjectSchema<{
3
3
  description: string;
4
4
  expires_at_millis: number;
5
5
  has_publishable_client_key: boolean;
@@ -12,7 +12,7 @@ export declare const apiKeysCreateInputSchema: import("yup").ObjectSchema<{
12
12
  has_secret_server_key: undefined;
13
13
  has_super_secret_admin_key: undefined;
14
14
  }, "">;
15
- export declare const apiKeysCreateOutputSchema: import("yup").ObjectSchema<{
15
+ export declare const internalApiKeysCreateOutputSchema: import("yup").ObjectSchema<{
16
16
  id: string;
17
17
  description: string;
18
18
  expires_at_millis: number;
@@ -32,7 +32,7 @@ export declare const apiKeysCreateOutputSchema: import("yup").ObjectSchema<{
32
32
  secret_server_key: undefined;
33
33
  super_secret_admin_key: undefined;
34
34
  }, "">;
35
- export declare const apiKeysCrudAdminObfuscatedReadSchema: import("yup").ObjectSchema<{
35
+ export declare const internalApiKeysCrudAdminObfuscatedReadSchema: import("yup").ObjectSchema<{
36
36
  id: string;
37
37
  description: string;
38
38
  expires_at_millis: number;
@@ -64,15 +64,15 @@ export declare const apiKeysCrudAdminObfuscatedReadSchema: import("yup").ObjectS
64
64
  last_four: undefined;
65
65
  };
66
66
  }, "">;
67
- export declare const apiKeysCrudAdminUpdateSchema: import("yup").ObjectSchema<{
67
+ export declare const internalApiKeysCrudAdminUpdateSchema: import("yup").ObjectSchema<{
68
68
  description: string | undefined;
69
69
  revoked: boolean | undefined;
70
70
  }, import("yup").AnyObject, {
71
71
  description: undefined;
72
72
  revoked: undefined;
73
73
  }, "">;
74
- export declare const apiKeysCrudAdminDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
75
- export declare const apiKeysCrud: import("../../crud").CrudSchemaFromOptions<{
74
+ export declare const internalApiKeysCrudAdminDeleteSchema: import("yup").MixedSchema<{} | undefined, import("yup").AnyObject, undefined, "">;
75
+ export declare const internalApiKeysCrud: import("../../crud").CrudSchemaFromOptions<{
76
76
  adminReadSchema: import("yup").ObjectSchema<{
77
77
  id: string;
78
78
  description: string;
@@ -131,4 +131,4 @@ export declare const apiKeysCrud: import("../../crud").CrudSchemaFromOptions<{
131
131
  };
132
132
  };
133
133
  }>;
134
- export type ApiKeysCrud = CrudTypeOf<typeof apiKeysCrud>;
134
+ export type InternalApiKeysCrud = CrudTypeOf<typeof internalApiKeysCrud>;
@@ -1,6 +1,6 @@
1
1
  import { createCrud } from "../../crud";
2
2
  import { yupBoolean, yupMixed, yupNumber, yupObject, yupString } from "../../schema-fields";
3
- const baseApiKeysReadSchema = yupObject({
3
+ const baseInternalApiKeysReadSchema = yupObject({
4
4
  id: yupString().defined(),
5
5
  description: yupString().defined(),
6
6
  expires_at_millis: yupNumber().defined(),
@@ -8,20 +8,20 @@ const baseApiKeysReadSchema = yupObject({
8
8
  created_at_millis: yupNumber().defined(),
9
9
  });
10
10
  // Used for the result of the create endpoint
11
- export const apiKeysCreateInputSchema = yupObject({
11
+ export const internalApiKeysCreateInputSchema = yupObject({
12
12
  description: yupString().defined(),
13
13
  expires_at_millis: yupNumber().defined(),
14
14
  has_publishable_client_key: yupBoolean().defined(),
15
15
  has_secret_server_key: yupBoolean().defined(),
16
16
  has_super_secret_admin_key: yupBoolean().defined(),
17
17
  });
18
- export const apiKeysCreateOutputSchema = baseApiKeysReadSchema.concat(yupObject({
18
+ export const internalApiKeysCreateOutputSchema = baseInternalApiKeysReadSchema.concat(yupObject({
19
19
  publishable_client_key: yupString().optional(),
20
20
  secret_server_key: yupString().optional(),
21
21
  super_secret_admin_key: yupString().optional(),
22
22
  }).defined());
23
23
  // Used for list, read and update endpoints after the initial creation
24
- export const apiKeysCrudAdminObfuscatedReadSchema = baseApiKeysReadSchema.concat(yupObject({
24
+ export const internalApiKeysCrudAdminObfuscatedReadSchema = baseInternalApiKeysReadSchema.concat(yupObject({
25
25
  publishable_client_key: yupObject({
26
26
  last_four: yupString().defined(),
27
27
  }).optional(),
@@ -32,15 +32,15 @@ export const apiKeysCrudAdminObfuscatedReadSchema = baseApiKeysReadSchema.concat
32
32
  last_four: yupString().defined(),
33
33
  }).optional(),
34
34
  }));
35
- export const apiKeysCrudAdminUpdateSchema = yupObject({
35
+ export const internalApiKeysCrudAdminUpdateSchema = yupObject({
36
36
  description: yupString().optional(),
37
37
  revoked: yupBoolean().oneOf([true]).optional(),
38
38
  }).defined();
39
- export const apiKeysCrudAdminDeleteSchema = yupMixed();
40
- export const apiKeysCrud = createCrud({
41
- adminReadSchema: apiKeysCrudAdminObfuscatedReadSchema,
42
- adminUpdateSchema: apiKeysCrudAdminUpdateSchema,
43
- adminDeleteSchema: apiKeysCrudAdminDeleteSchema,
39
+ export const internalApiKeysCrudAdminDeleteSchema = yupMixed();
40
+ export const internalApiKeysCrud = createCrud({
41
+ adminReadSchema: internalApiKeysCrudAdminObfuscatedReadSchema,
42
+ adminUpdateSchema: internalApiKeysCrudAdminUpdateSchema,
43
+ adminDeleteSchema: internalApiKeysCrudAdminDeleteSchema,
44
44
  docs: {
45
45
  adminList: {
46
46
  hidden: true,