@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @stackframe/stack-shared
2
2
 
3
+ ## 2.8.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Various changes
8
+
9
+ ## 2.8.4
10
+
11
+ ### Patch Changes
12
+
13
+ - Various changes
14
+
15
+ ## 2.8.3
16
+
17
+ ### Patch Changes
18
+
19
+ - Various changes
20
+
3
21
  ## 2.8.2
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,38 @@
1
+ export type ConfigValue = string | number | boolean | null | ConfigValue[] | Config;
2
+ export type Config = {
3
+ [keyOrDotNotation: string]: ConfigValue | undefined;
4
+ };
5
+ export type NormalizedConfigValue = string | number | boolean | NormalizedConfig | NormalizedConfigValue[];
6
+ export type NormalizedConfig = {
7
+ [key: string]: NormalizedConfigValue | undefined;
8
+ };
9
+ export type _NormalizesTo<N> = N extends object ? (Config & {
10
+ [K in keyof N]?: _NormalizesTo<N[K]> | null;
11
+ } & {
12
+ [K in `${string}.${string}`]: ConfigValue;
13
+ }) : N;
14
+ export type NormalizesTo<N extends NormalizedConfig> = _NormalizesTo<N>;
15
+ /**
16
+ * Note that a config can both be valid and not normalizable.
17
+ */
18
+ export declare function isValidConfig(c: unknown): c is Config;
19
+ export declare function getInvalidConfigReason(c: unknown, options?: {
20
+ configName?: string;
21
+ }): string | undefined;
22
+ export declare function assertValidConfig(c: unknown): void;
23
+ export declare function override(c1: Config, ...configs: Config[]): Config;
24
+ type NormalizeOptions = {
25
+ /**
26
+ * What to do if a dot notation is used on null.
27
+ *
28
+ * - "empty" (default): Replace the null with an empty object.
29
+ * - "throw": Throw an error.
30
+ * - "ignore": Ignore the dot notation field.
31
+ */
32
+ onDotIntoNull?: "empty" | "throw" | "ignore";
33
+ };
34
+ export declare class NormalizationError extends Error {
35
+ constructor(...args: ConstructorParameters<typeof Error>);
36
+ }
37
+ export declare function normalize(c: Config, options?: NormalizeOptions): NormalizedConfig;
38
+ export {};
@@ -0,0 +1,224 @@
1
+ // see https://github.com/stack-auth/info/blob/main/eng-handbook/random-thoughts/config-json-format.md
2
+ import { StackAssertionError, throwErr } from "../utils/errors";
3
+ import { deleteKey, filterUndefined, get, hasAndNotUndefined, set } from "../utils/objects";
4
+ /**
5
+ * Note that a config can both be valid and not normalizable.
6
+ */
7
+ export function isValidConfig(c) {
8
+ return getInvalidConfigReason(c) === undefined;
9
+ }
10
+ export function getInvalidConfigReason(c, options = {}) {
11
+ const configName = options.configName ?? 'config';
12
+ if (c === null || typeof c !== 'object')
13
+ return `${configName} must be a non-null object`;
14
+ for (const [key, value] of Object.entries(c)) {
15
+ if (value === undefined)
16
+ continue;
17
+ if (typeof key !== 'string')
18
+ return `${configName} must have only string keys (found: ${typeof key})`;
19
+ if (!key.match(/^[a-zA-Z0-9_:$][a-zA-Z_:$0-9\-]*(?:\.[a-zA-Z0-9_:$][a-zA-Z_:$0-9\-]*)*$/))
20
+ return `All keys of ${configName} must consist of only alphanumeric characters, dots, underscores, colons, dollar signs, or hyphens and start with a character other than a hyphen (found: ${key})`;
21
+ const entryName = `${configName}.${key}`;
22
+ const reason = getInvalidConfigValueReason(value, { valueName: entryName });
23
+ if (reason)
24
+ return reason;
25
+ }
26
+ return undefined;
27
+ }
28
+ function getInvalidConfigValueReason(value, options = {}) {
29
+ const valueName = options.valueName ?? 'value';
30
+ switch (typeof value) {
31
+ case 'string':
32
+ case 'number':
33
+ case 'boolean': {
34
+ break;
35
+ }
36
+ case 'object': {
37
+ if (value === null) {
38
+ break;
39
+ }
40
+ else if (Array.isArray(value)) {
41
+ for (const [index, v] of value.entries()) {
42
+ const reason = getInvalidConfigValueReason(v, { valueName: `${valueName}[${index}]` });
43
+ if (reason)
44
+ return reason;
45
+ }
46
+ }
47
+ else {
48
+ const reason = getInvalidConfigReason(value, { configName: valueName });
49
+ if (reason)
50
+ return reason;
51
+ }
52
+ break;
53
+ }
54
+ default: {
55
+ return `${valueName} has an invalid value type ${typeof value} (value: ${value})`;
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+ export function assertValidConfig(c) {
61
+ const reason = getInvalidConfigReason(c);
62
+ if (reason)
63
+ throw new StackAssertionError(`Invalid config: ${reason}`, { c });
64
+ }
65
+ export function override(c1, ...configs) {
66
+ if (configs.length === 0)
67
+ return c1;
68
+ if (configs.length > 1)
69
+ return override(override(c1, configs[0]), ...configs.slice(1));
70
+ const c2 = configs[0];
71
+ assertValidConfig(c1);
72
+ assertValidConfig(c2);
73
+ let result = c1;
74
+ for (const key of Object.keys(filterUndefined(c2))) {
75
+ result = Object.fromEntries(Object.entries(result).filter(([k]) => k !== key && !k.startsWith(key + '.')));
76
+ }
77
+ return {
78
+ ...result,
79
+ ...filterUndefined(c2),
80
+ };
81
+ }
82
+ import.meta.vitest?.test("override(...)", ({ expect }) => {
83
+ expect(override({
84
+ a: 1,
85
+ b: 2,
86
+ "c.d": 3,
87
+ "c.e.f": 4,
88
+ "c.g": 5,
89
+ h: [6, { i: 7 }, 8],
90
+ k: 123,
91
+ l: undefined,
92
+ }, {
93
+ a: 9,
94
+ "c.d": 10,
95
+ "c.e": null,
96
+ "h.0": 11,
97
+ "h.1": {
98
+ j: 12,
99
+ },
100
+ k: undefined,
101
+ })).toEqual({
102
+ a: 9,
103
+ b: 2,
104
+ "c.d": 10,
105
+ "c.e": null,
106
+ "c.g": 5,
107
+ h: [6, { i: 7 }, 8],
108
+ "h.0": 11,
109
+ "h.1": {
110
+ j: 12,
111
+ },
112
+ k: 123,
113
+ l: undefined,
114
+ });
115
+ });
116
+ export class NormalizationError extends Error {
117
+ constructor(...args) {
118
+ super(...args);
119
+ }
120
+ }
121
+ NormalizationError.prototype.name = "NormalizationError";
122
+ export function normalize(c, options = {}) {
123
+ assertValidConfig(c);
124
+ const onDotIntoNull = options.onDotIntoNull ?? "empty";
125
+ const countDots = (s) => s.match(/\./g)?.length ?? 0;
126
+ const result = {};
127
+ const keysByDepth = Object.keys(c).sort((a, b) => countDots(a) - countDots(b));
128
+ outer: for (const key of keysByDepth) {
129
+ const keySegmentsWithoutLast = key.split('.');
130
+ const last = keySegmentsWithoutLast.pop() ?? throwErr('split returns empty array?');
131
+ const value = get(c, key);
132
+ if (value === undefined)
133
+ continue;
134
+ let current = result;
135
+ for (const keySegment of keySegmentsWithoutLast) {
136
+ if (!hasAndNotUndefined(current, keySegment)) {
137
+ switch (onDotIntoNull) {
138
+ case "empty": {
139
+ set(current, keySegment, {});
140
+ break;
141
+ }
142
+ case "throw": {
143
+ throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} doesn't exist on the object (or is null). Maybe this config is not normalizable?`);
144
+ }
145
+ case "ignore": {
146
+ continue outer;
147
+ }
148
+ }
149
+ }
150
+ const value = get(current, keySegment);
151
+ if (typeof value !== 'object') {
152
+ throw new NormalizationError(`Tried to use dot notation to access ${JSON.stringify(key)}, but ${JSON.stringify(keySegment)} is not an object. Maybe this config is not normalizable?`);
153
+ }
154
+ current = value;
155
+ }
156
+ setNormalizedValue(current, last, value);
157
+ }
158
+ return result;
159
+ }
160
+ function normalizeValue(value) {
161
+ if (value === null)
162
+ throw new NormalizationError("Tried to normalize a null value");
163
+ if (Array.isArray(value))
164
+ return value.map(normalizeValue);
165
+ if (typeof value === 'object')
166
+ return normalize(value);
167
+ return value;
168
+ }
169
+ function setNormalizedValue(result, key, value) {
170
+ if (value === null) {
171
+ if (hasAndNotUndefined(result, key)) {
172
+ deleteKey(result, key);
173
+ }
174
+ }
175
+ else {
176
+ set(result, key, normalizeValue(value));
177
+ }
178
+ }
179
+ import.meta.vitest?.test("normalize(...)", ({ expect }) => {
180
+ expect(normalize({
181
+ a: 9,
182
+ b: 2,
183
+ c: {},
184
+ "c.d": 10,
185
+ "c.e": null,
186
+ "c.g": 5,
187
+ h: [6, { i: 7 }, 8],
188
+ "h.0": 11,
189
+ "h.1": {
190
+ j: 12,
191
+ },
192
+ k: { l: {} },
193
+ "k.l.m": 13,
194
+ n: undefined,
195
+ })).toEqual({
196
+ a: 9,
197
+ b: 2,
198
+ c: {
199
+ d: 10,
200
+ g: 5,
201
+ },
202
+ h: [11, { j: 12 }, 8],
203
+ k: { l: { m: 13 } },
204
+ });
205
+ // dotting into null
206
+ expect(normalize({
207
+ "b.c": 2,
208
+ })).toEqual({ b: { c: 2 } });
209
+ expect(() => normalize({
210
+ "b.c": 2,
211
+ }, { onDotIntoNull: "throw" })).toThrow(`Tried to use dot notation to access "b.c", but "b" doesn't exist on the object (or is null). Maybe this config is not normalizable?`);
212
+ expect(() => normalize({
213
+ b: null,
214
+ "b.c": 2,
215
+ }, { onDotIntoNull: "throw" })).toThrow(`Tried to use dot notation to access "b.c", but "b" doesn't exist on the object (or is null). Maybe this config is not normalizable?`);
216
+ expect(normalize({
217
+ "b.c": 2,
218
+ }, { onDotIntoNull: "ignore" })).toEqual({});
219
+ // dotting into non-object
220
+ expect(() => normalize({
221
+ b: 1,
222
+ "b.c": 2,
223
+ })).toThrow(`Tried to use dot notation to access "b.c", but "b" is not an object. Maybe this config is not normalizable?`);
224
+ });