@bool-ts/guard-sdk 1.0.2-beta.5 → 1.1.0-beta.1

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 (67) hide show
  1. package/dist/decorators/actionGuard.decorator.d.ts +3 -1
  2. package/dist/decorators/controllerGuard.decorator.d.ts +2 -1
  3. package/dist/decorators/index.d.ts +0 -2
  4. package/dist/definers/index.d.ts +1 -0
  5. package/dist/definers/policies.d.ts +5 -0
  6. package/dist/definers/resources.d.ts +8 -0
  7. package/dist/entities/loader.d.ts +7 -2
  8. package/dist/index.js +12 -12
  9. package/dist/index.js.map +9 -11
  10. package/dist/instances/client.d.ts +7 -4
  11. package/dist/interfaces/client.interface.d.ts +9 -4
  12. package/dist/interfaces/index.d.ts +1 -1
  13. package/dist/types/actionGuard.d.ts +8 -0
  14. package/dist/types/controllerGuard.d.ts +6 -0
  15. package/dist/types/index.d.ts +3 -0
  16. package/dist/types/policies.d.ts +11 -0
  17. package/dist/types/resources.d.ts +6 -0
  18. package/dist/ultils/functions/deepFreeze.d.ts +7 -0
  19. package/dist/ultils/functions/index.d.ts +1 -0
  20. package/dist/ultils/types/constructor.d.ts +1 -0
  21. package/dist/ultils/types/enforceUnique.d.ts +8 -0
  22. package/dist/ultils/types/error.d.ts +3 -0
  23. package/dist/ultils/types/extractTuple.d.ts +1 -0
  24. package/dist/ultils/types/inArray.d.ts +1 -0
  25. package/dist/ultils/types/index.d.ts +14 -0
  26. package/dist/ultils/types/mergeTuple.d.ts +3 -0
  27. package/dist/ultils/types/noneEmptyArray.d.ts +1 -0
  28. package/dist/ultils/types/partialTuple.d.ts +1 -0
  29. package/dist/ultils/types/partialTupleUnordered.d.ts +3 -0
  30. package/dist/ultils/types/partialTupleUnorderedNonEmpty.d.ts +3 -0
  31. package/dist/ultils/types/partialTurpleNonEmpty.d.ts +5 -0
  32. package/dist/ultils/types/permutationTuple.d.ts +1 -0
  33. package/dist/ultils/types/shuffleTuple.d.ts +5 -0
  34. package/dist/ultils/types/strictPartial.d.ts +5 -0
  35. package/package.json +4 -4
  36. package/src/decorators/actionGuard.decorator.ts +18 -3
  37. package/src/decorators/controllerGuard.decorator.ts +7 -3
  38. package/src/decorators/index.ts +0 -2
  39. package/src/definers/index.ts +1 -0
  40. package/src/definers/policies.ts +24 -0
  41. package/src/definers/resources.ts +47 -0
  42. package/src/entities/loader.ts +17 -3
  43. package/src/instances/client.ts +135 -57
  44. package/src/interfaces/client.interface.ts +18 -6
  45. package/src/interfaces/index.ts +2 -1
  46. package/src/types/actionGuard.ts +11 -0
  47. package/src/types/controllerGuard.ts +8 -0
  48. package/src/types/index.ts +3 -0
  49. package/src/types/policies.ts +36 -0
  50. package/src/types/resources.ts +10 -0
  51. package/src/ultils/functions/deepFreeze.ts +19 -0
  52. package/src/ultils/functions/index.ts +1 -0
  53. package/src/ultils/types/constructor.ts +1 -0
  54. package/src/ultils/types/enforceUnique.ts +30 -0
  55. package/src/ultils/types/error.ts +1 -0
  56. package/src/ultils/types/extractTuple.ts +1 -0
  57. package/src/ultils/types/inArray.ts +7 -0
  58. package/src/ultils/types/index.ts +14 -0
  59. package/src/ultils/types/mergeTuple.ts +3 -0
  60. package/src/ultils/types/noneEmptyArray.ts +1 -0
  61. package/src/ultils/types/partialTuple.ts +3 -0
  62. package/src/ultils/types/partialTupleUnordered.ts +8 -0
  63. package/src/ultils/types/partialTupleUnorderedNonEmpty.ts +9 -0
  64. package/src/ultils/types/partialTurpleNonEmpty.ts +8 -0
  65. package/src/ultils/types/permutationTuple.ts +5 -0
  66. package/src/ultils/types/shuffleTuple.ts +7 -0
  67. package/src/ultils/types/strictPartial.ts +5 -0
@@ -1,5 +1,10 @@
1
1
  import type { TApiResponse } from "../interfaces/base";
2
- import type { IClient, TClientConfigs, TClientOptions } from "../interfaces/client.interface";
2
+ import type {
3
+ IClient,
4
+ TClientCredential,
5
+ TClientDefinition,
6
+ TClientOptions
7
+ } from "../interfaces/client.interface";
3
8
 
4
9
  import { SignJWT } from "jose";
5
10
  import { email } from "zod/v4";
@@ -7,6 +12,7 @@ import { Enums } from "../constants";
7
12
 
8
13
  export class Client implements IClient {
9
14
  readonly #baseUri = Enums.ETokenAudiences.SYSTEM;
15
+ readonly #defaultVersion: number = 1;
10
16
 
11
17
  private token: string | undefined;
12
18
 
@@ -16,8 +22,11 @@ export class Client implements IClient {
16
22
  * @param options
17
23
  */
18
24
  constructor(
19
- private readonly configs: TClientConfigs,
20
- private readonly options?: TClientOptions
25
+ private readonly params: Readonly<{
26
+ credential: TClientCredential;
27
+ definition?: TClientDefinition;
28
+ options?: TClientOptions;
29
+ }>
21
30
  ) {}
22
31
 
23
32
  /**
@@ -25,8 +34,16 @@ export class Client implements IClient {
25
34
  * @returns
26
35
  */
27
36
  async signToken() {
37
+ const {
38
+ params: {
39
+ credential: { tenantId, appId, modeId, secretKey },
40
+ options
41
+ }
42
+ } = this;
43
+
28
44
  try {
29
- const rawKey = new Uint8Array(Buffer.from(this.configs.secretKey, "base64"));
45
+ const rawKey = new Uint8Array(Buffer.from(secretKey, "base64"));
46
+
30
47
  const privateKey = await crypto.subtle.importKey(
31
48
  "raw",
32
49
  rawKey,
@@ -36,10 +53,10 @@ export class Client implements IClient {
36
53
  );
37
54
 
38
55
  const jwt = await new SignJWT({
39
- tenantId: this.configs.tenantId,
40
- appId: this.configs.appId,
41
- modeId: this.configs.modeId,
42
- iss: this.configs.tenantId,
56
+ tenantId: tenantId,
57
+ appId: appId,
58
+ modeId: modeId,
59
+ iss: tenantId,
43
60
  aud: Enums.ETokenAudiences.SYSTEM
44
61
  })
45
62
  .setProtectedHeader({ alg: "HS512" })
@@ -49,7 +66,7 @@ export class Client implements IClient {
49
66
 
50
67
  return jwt;
51
68
  } catch (error) {
52
- if (this.options?.logs) {
69
+ if (options?.logs) {
53
70
  console.error("[BoolGuard] Sign token error:", error);
54
71
  }
55
72
 
@@ -63,17 +80,27 @@ export class Client implements IClient {
63
80
  * @returns
64
81
  */
65
82
  async ping() {
66
- try {
67
- const authToken = this.token || (await this.signToken());
83
+ const {
84
+ token,
85
+ params: {
86
+ credential: { tenantId, appId, modeId },
87
+ options
88
+ }
89
+ } = this;
68
90
 
91
+ try {
92
+ const authToken = token || (await this.signToken());
69
93
  const requestHeaders = new Headers();
70
- requestHeaders.append("X-Tenant-ID", this.configs.tenantId);
71
- requestHeaders.append("X-App-ID", this.configs.appId);
72
- requestHeaders.append("X-Mode-ID", this.configs.modeId);
94
+
95
+ requestHeaders.append("X-Tenant-ID", tenantId);
96
+ requestHeaders.append("X-App-ID", appId);
97
+ requestHeaders.append("X-Mode-ID", modeId);
73
98
  requestHeaders.append("Authorization", `Bearer ${authToken}`);
74
99
 
75
100
  const response = await fetch(
76
- `${this.#baseUri}/v${this.options?.version}/tenant-app-modes/ping`,
101
+ `${this.#baseUri}/v${
102
+ options?.version || this.#defaultVersion
103
+ }/tenant-app-modes/ping`,
77
104
  {
78
105
  method: "GET",
79
106
  headers: requestHeaders
@@ -86,7 +113,7 @@ export class Client implements IClient {
86
113
 
87
114
  return response.ok;
88
115
  } catch (error) {
89
- if (this.options?.logs) {
116
+ if (options?.logs) {
90
117
  console.error("[BoolGuard] Ping error:", error);
91
118
  }
92
119
 
@@ -106,28 +133,38 @@ export class Client implements IClient {
106
133
  }: Parameters<IClient["createPlainAccount"]>[number]): ReturnType<
107
134
  IClient["createPlainAccount"]
108
135
  > {
109
- try {
110
- const authToken = this.token || (await this.signToken());
136
+ const {
137
+ token,
138
+ params: {
139
+ credential: { tenantId, appId, modeId },
140
+ options
141
+ }
142
+ } = this;
111
143
 
144
+ try {
145
+ const authToken = token || (await this.signToken());
112
146
  const requestHeaders = new Headers();
113
- requestHeaders.append("X-Tenant-ID", this.configs.tenantId);
114
- requestHeaders.append("X-App-ID", this.configs.appId);
115
- requestHeaders.append("X-Mode-ID", this.configs.modeId);
147
+
148
+ requestHeaders.append("X-Tenant-ID", tenantId);
149
+ requestHeaders.append("X-App-ID", appId);
150
+ requestHeaders.append("X-Mode-ID", modeId);
116
151
  requestHeaders.append("Authorization", `Bearer ${authToken}`);
117
152
  requestHeaders.append("Content-Type", "application/json");
118
153
 
119
154
  const response = await fetch(
120
- `${this.#baseUri}/v${this.options?.version}/tenant-app-mode-accounts`,
155
+ `${this.#baseUri}/v${
156
+ options?.version || this.#defaultVersion
157
+ }/tenant-app-mode-accounts`,
121
158
  {
122
159
  method: "POST",
123
160
  headers: requestHeaders,
124
161
  body: JSON.stringify({
125
- data: {
162
+ data: Object.freeze({
126
163
  type: "plain",
127
164
  identity: identity,
128
165
  password: password,
129
166
  metadata: metadata
130
- }
167
+ })
131
168
  })
132
169
  }
133
170
  );
@@ -136,14 +173,16 @@ export class Client implements IClient {
136
173
  throw await response.json();
137
174
  }
138
175
 
139
- const { data } = (await response.json()) as TApiResponse<IClient["createPlainAccount"]>;
176
+ const { data } = (await response.json()) as TApiResponse<
177
+ IClient["createPlainAccount"]
178
+ >;
140
179
 
141
180
  return Object.freeze({
142
181
  account: data.account,
143
182
  credential: data.credential
144
183
  });
145
184
  } catch (error) {
146
- if (this.options?.logs) {
185
+ if (options?.logs) {
147
186
  console.error("[BoolGuard] Create plain account error:", error);
148
187
  }
149
188
 
@@ -163,28 +202,38 @@ export class Client implements IClient {
163
202
  }: Parameters<IClient["createEmailAccount"]>[number]): ReturnType<
164
203
  IClient["createEmailAccount"]
165
204
  > {
166
- try {
167
- const authToken = this.token || (await this.signToken());
205
+ const {
206
+ token,
207
+ params: {
208
+ credential: { tenantId, appId, modeId },
209
+ options
210
+ }
211
+ } = this;
168
212
 
213
+ try {
214
+ const authToken = token || (await this.signToken());
169
215
  const requestHeaders = new Headers();
170
- requestHeaders.append("X-Tenant-ID", this.configs.tenantId);
171
- requestHeaders.append("X-App-ID", this.configs.appId);
172
- requestHeaders.append("X-Mode-ID", this.configs.modeId);
216
+
217
+ requestHeaders.append("X-Tenant-ID", tenantId);
218
+ requestHeaders.append("X-App-ID", appId);
219
+ requestHeaders.append("X-Mode-ID", modeId);
173
220
  requestHeaders.append("Authorization", `Bearer ${authToken}`);
174
221
  requestHeaders.append("Content-Type", "application/json");
175
222
 
176
223
  const response = await fetch(
177
- `${this.#baseUri}/v${this.options?.version}/tenant-app-mode-accounts`,
224
+ `${this.#baseUri}/v${
225
+ options?.version || this.#defaultVersion
226
+ }/tenant-app-mode-accounts`,
178
227
  {
179
228
  method: "POST",
180
229
  headers: requestHeaders,
181
230
  body: JSON.stringify({
182
- data: {
231
+ data: Object.freeze({
183
232
  type: "email",
184
233
  identity: identity,
185
234
  password: password,
186
235
  metadata: metadata
187
- }
236
+ })
188
237
  })
189
238
  }
190
239
  );
@@ -193,14 +242,16 @@ export class Client implements IClient {
193
242
  throw await response.json();
194
243
  }
195
244
 
196
- const { data } = (await response.json()) as TApiResponse<IClient["createEmailAccount"]>;
245
+ const { data } = (await response.json()) as TApiResponse<
246
+ IClient["createEmailAccount"]
247
+ >;
197
248
 
198
249
  return Object.freeze({
199
250
  account: data.account,
200
251
  credential: data.credential
201
252
  });
202
253
  } catch (error) {
203
- if (this.options?.logs) {
254
+ if (options?.logs) {
204
255
  console.error("[BoolGuard] Create email account error:", error);
205
256
  }
206
257
 
@@ -216,29 +267,41 @@ export class Client implements IClient {
216
267
  async authenticate({
217
268
  identity,
218
269
  password
219
- }: Parameters<IClient["authenticate"]>[number]): ReturnType<IClient["authenticate"]> {
270
+ }: Parameters<IClient["authenticate"]>[number]): ReturnType<
271
+ IClient["authenticate"]
272
+ > {
273
+ const {
274
+ token,
275
+ params: {
276
+ credential: { tenantId, appId, modeId },
277
+ options
278
+ }
279
+ } = this;
280
+
220
281
  try {
221
- const authToken = this.token || (await this.signToken());
282
+ const authToken = token || (await this.signToken());
222
283
  const emailValidation = await email().safeParseAsync(identity);
223
-
224
284
  const requestHeaders = new Headers();
225
- requestHeaders.append("X-Tenant-ID", this.configs.tenantId);
226
- requestHeaders.append("X-App-ID", this.configs.appId);
227
- requestHeaders.append("X-Mode-ID", this.configs.modeId);
285
+
286
+ requestHeaders.append("X-Tenant-ID", tenantId);
287
+ requestHeaders.append("X-App-ID", appId);
288
+ requestHeaders.append("X-Mode-ID", modeId);
228
289
  requestHeaders.append("Authorization", `Bearer ${authToken}`);
229
290
  requestHeaders.append("Content-Type", "application/json");
230
291
 
231
292
  const response = await fetch(
232
- `${this.#baseUri}/v${this.options?.version}/tenant-app-mode-accounts/authenticate`,
293
+ `${this.#baseUri}/v${
294
+ options?.version || this.#defaultVersion
295
+ }/tenant-app-mode-accounts/authenticate`,
233
296
  {
234
297
  method: "POST",
235
298
  headers: requestHeaders,
236
299
  body: JSON.stringify({
237
- data: {
300
+ data: Object.freeze({
238
301
  type: !emailValidation.success ? "plain" : "email",
239
302
  identity: identity,
240
303
  password: password
241
- }
304
+ })
242
305
  })
243
306
  }
244
307
  );
@@ -247,7 +310,9 @@ export class Client implements IClient {
247
310
  throw await response.json();
248
311
  }
249
312
 
250
- const { data } = (await response.json()) as TApiResponse<IClient["authenticate"]>;
313
+ const { data } = (await response.json()) as TApiResponse<
314
+ IClient["authenticate"]
315
+ >;
251
316
 
252
317
  return Object.freeze({
253
318
  account: data.account,
@@ -255,7 +320,7 @@ export class Client implements IClient {
255
320
  token: data.token
256
321
  });
257
322
  } catch (error) {
258
- if (this.options?.logs) {
323
+ if (options?.logs) {
259
324
  console.error("[BoolGuard] Authenticate error:", error);
260
325
  }
261
326
 
@@ -269,26 +334,37 @@ export class Client implements IClient {
269
334
  */
270
335
  async verifyToken({
271
336
  token
272
- }: Parameters<IClient["verifyToken"]>[number]): ReturnType<IClient["verifyToken"]> {
337
+ }: Parameters<IClient["verifyToken"]>[number]): ReturnType<
338
+ IClient["verifyToken"]
339
+ > {
340
+ const {
341
+ params: {
342
+ credential: { tenantId, appId, modeId },
343
+ options
344
+ }
345
+ } = this;
346
+
273
347
  try {
274
348
  const authToken = this.token || (await this.signToken());
275
-
276
349
  const requestHeaders = new Headers();
277
- requestHeaders.append("X-Tenant-ID", this.configs.tenantId);
278
- requestHeaders.append("X-App-ID", this.configs.appId);
279
- requestHeaders.append("X-Mode-ID", this.configs.modeId);
350
+
351
+ requestHeaders.append("X-Tenant-ID", tenantId);
352
+ requestHeaders.append("X-App-ID", appId);
353
+ requestHeaders.append("X-Mode-ID", modeId);
280
354
  requestHeaders.append("Authorization", `Bearer ${authToken}`);
281
355
  requestHeaders.append("Content-Type", "application/json");
282
356
 
283
357
  const response = await fetch(
284
- `${this.#baseUri}/v${this.options?.version}/tenant-app-mode-accounts/verify`,
358
+ `${this.#baseUri}/v${
359
+ options?.version || this.#defaultVersion
360
+ }/tenant-app-mode-accounts/verify`,
285
361
  {
286
362
  method: "POST",
287
363
  headers: requestHeaders,
288
364
  body: JSON.stringify({
289
- data: {
365
+ data: Object.freeze({
290
366
  token: token
291
- }
367
+ })
292
368
  })
293
369
  }
294
370
  );
@@ -297,14 +373,16 @@ export class Client implements IClient {
297
373
  throw await response.json();
298
374
  }
299
375
 
300
- const { data } = (await response.json()) as TApiResponse<IClient["verifyToken"]>;
376
+ const { data } = (await response.json()) as TApiResponse<
377
+ IClient["verifyToken"]
378
+ >;
301
379
 
302
380
  return Object.freeze({
303
381
  account: data.account,
304
382
  credential: data.credential
305
383
  });
306
384
  } catch (error) {
307
- if (this.options?.logs) {
385
+ if (options?.logs) {
308
386
  console.error("[BoolGuard] Verify token error:", error);
309
387
  }
310
388
 
@@ -1,17 +1,23 @@
1
+ import type { defineResources } from "../definers";
1
2
  import type { IAccount, TDefaultAccountMetadata } from "./account.interface";
2
3
  import type { IAccountCredential } from "./accountCredential.interface";
3
4
 
4
- export type TClientConfigs = {
5
+ export type TClientCredential = Readonly<{
5
6
  tenantId: string;
6
7
  appId: string;
7
8
  modeId: string;
8
9
  secretKey: string;
9
- };
10
+ }>;
11
+
12
+ export type TClientDefinition = Readonly<{
13
+ resources: ReturnType<typeof defineResources>["resources"];
14
+ policies: ReturnType<ReturnType<typeof defineResources>["definePolicies"]>;
15
+ }>;
10
16
 
11
- export type TClientOptions = {
17
+ export type TClientOptions = Readonly<{
12
18
  version?: 1;
13
19
  logs?: boolean;
14
- };
20
+ }>;
15
21
 
16
22
  export interface IClient<
17
23
  TAccountMetadata extends TDefaultAccountMetadata = TDefaultAccountMetadata
@@ -36,14 +42,20 @@ export interface IClient<
36
42
  credential: IAccountCredential;
37
43
  }>
38
44
  >;
39
- authenticate(args: { identity: string; password?: string | null }): Promise<{
45
+ authenticate(args: {
46
+ identity: string;
47
+ password?: string | null;
48
+ }): Promise<{
40
49
  token: string;
41
50
  account: IAccount<TAccountMetadata>;
42
51
  credential: IAccountCredential;
43
52
  }>;
44
53
  verifyToken(args: {
45
54
  token: string;
46
- }): Promise<{ account: IAccount<TAccountMetadata>; credential: IAccountCredential }>;
55
+ }): Promise<{
56
+ account: IAccount<TAccountMetadata>;
57
+ credential: IAccountCredential;
58
+ }>;
47
59
  }
48
60
 
49
61
  export type TAuthState = {
@@ -1,4 +1,5 @@
1
1
  export type { IAccount } from "./account.interface";
2
2
  export type { IAccountCredential } from "./accountCredential.interface";
3
3
  export type { TApiResponse } from "./base";
4
- export type { IClient, TAuthState, TClientConfigs, TClientOptions } from "./client.interface";
4
+ export type { IClient, TAuthState, TClientCredential as TClientConfigs, TClientOptions } from "./client.interface";
5
+
@@ -0,0 +1,11 @@
1
+ import type { TOptionalTupleUnorderedNonEmpty } from "../ultils/types";
2
+ import type { TResourceDefinition } from "./resources";
3
+
4
+ export type TActionGuardOptions<T extends readonly TResourceDefinition[]> = {
5
+ [R in T[number] as R["type"]]: {
6
+ resource: R["type"];
7
+ action:
8
+ | R["actions"][number]
9
+ | TOptionalTupleUnorderedNonEmpty<R["actions"]>;
10
+ };
11
+ }[T[number]["type"]];
@@ -0,0 +1,8 @@
1
+ import type { TResourceDefinition } from "./resources";
2
+
3
+ export type TControllerGuardOptions<T extends readonly TResourceDefinition[]> =
4
+ {
5
+ [R in T[number] as R["type"]]: {
6
+ resource: R["type"];
7
+ };
8
+ }[T[number]["type"]];
@@ -0,0 +1,3 @@
1
+ export * from "./actionGuard";
2
+ export * from "./policies";
3
+ export * from "./resources";
@@ -0,0 +1,36 @@
1
+ import type { TResourceDefinition } from "./resources";
2
+
3
+ export type TPolicyDefinition<
4
+ T extends readonly TResourceDefinition[] = readonly TResourceDefinition[]
5
+ > = {
6
+ readonly alias: string;
7
+ readonly effect: "permit" | "deny";
8
+ readonly [key: string]: unknown;
9
+ } & {
10
+ [R in T[number] as R["type"]]: {
11
+ readonly resource: R["type"];
12
+ readonly action: R["actions"][number];
13
+ };
14
+ }[T[number]["type"]];
15
+
16
+ // export type TPolicyAttributes<
17
+ // T extends readonly TResourceDefinition[],
18
+ // K extends readonly TPolicyDefinition[] = readonly TPolicyDefinition[]
19
+ // > = {
20
+ // [R in K[number] as R["alias"]]: {
21
+ // readonly alias: string;
22
+ // readonly effect: "permit" | "deny";
23
+ // } & {
24
+ // [R in T[number] as R["type"]]: {
25
+ // readonly resource: R["type"];
26
+ // readonly action: R["actions"][number];
27
+ // };
28
+ // }[T[number]["type"]];
29
+ // }[K[number]["alias"]];
30
+
31
+ // {
32
+ // [R in K[number] as R["type"]]: {
33
+ // readonly resource: R["type"];
34
+ // readonly action: R["actions"][number];
35
+ // };
36
+ // }[K[number]["type"]];
@@ -0,0 +1,10 @@
1
+ import type { TNonEmptyArray } from "../ultils/types";
2
+
3
+ export type TResourceDefinition<
4
+ T extends string = string,
5
+ A extends TNonEmptyArray<string> = TNonEmptyArray<string>
6
+ > = {
7
+ readonly type: T;
8
+ readonly actions: A;
9
+ readonly [key: string]: unknown;
10
+ };
@@ -0,0 +1,19 @@
1
+ type DeepReadonly<T> = T extends (...args: any[]) => any
2
+ ? T
3
+ : T extends readonly any[]
4
+ ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
5
+ : T extends object
6
+ ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
7
+ : T;
8
+
9
+ export function deepFreeze<const T>(obj: T): DeepReadonly<T> {
10
+ if (typeof obj !== "object" || obj === null) return obj as any;
11
+ Object.freeze(obj);
12
+ for (const key of Object.keys(obj)) {
13
+ const val = (obj as any)[key];
14
+ if (typeof val === "object" && val !== null && !Object.isFrozen(val)) {
15
+ deepFreeze(val);
16
+ }
17
+ }
18
+ return obj as any;
19
+ }
@@ -0,0 +1 @@
1
+ export { deepFreeze } from "./deepFreeze";
@@ -0,0 +1 @@
1
+ export type TConstructor<T, K extends any[] = any[]> = new (...args: K) => T;
@@ -0,0 +1,30 @@
1
+ type ValuesOfKey<Arr extends any[], K extends keyof any> = Arr extends [
2
+ infer Head,
3
+ ...infer Tail
4
+ ]
5
+ ? Head extends Record<K, any>
6
+ ? [Head[K], ...ValuesOfKey<Tail, K>]
7
+ : ValuesOfKey<Tail, K>
8
+ : [];
9
+
10
+ type Includes<Arr extends any[], V> = Arr extends [infer H, ...infer R]
11
+ ? [V] extends [H]
12
+ ? true
13
+ : Includes<R, V>
14
+ : false;
15
+
16
+ type HasDuplicate<
17
+ Arr extends readonly any[],
18
+ K extends keyof any
19
+ > = Arr extends [infer Head, ...infer Tail]
20
+ ? Head extends Record<K, any>
21
+ ? Includes<ValuesOfKey<Tail, K>, Head[K]> extends true
22
+ ? true
23
+ : HasDuplicate<Tail, K>
24
+ : HasDuplicate<Tail, K>
25
+ : false;
26
+
27
+ export type TEnforceUnique<
28
+ Arr extends readonly any[],
29
+ K extends keyof Arr[number]
30
+ > = HasDuplicate<Arr, K> extends true ? ["Duplicate key found"] : Arr;
@@ -0,0 +1 @@
1
+ export type TError<Message extends string> = { __error__: Message };
@@ -0,0 +1 @@
1
+ export type ExtractTuple<T> = T extends readonly [...infer U] ? U : never;
@@ -0,0 +1,7 @@
1
+ export type TInArray<T, X> = T extends readonly [X, ...infer _Rest]
2
+ ? true
3
+ : T extends readonly [X]
4
+ ? true
5
+ : T extends readonly [infer _, ...infer Rest]
6
+ ? TInArray<Rest, X>
7
+ : false;
@@ -0,0 +1,14 @@
1
+ export type { TConstructor } from "./constructor";
2
+ export type { TEnforceUnique } from "./enforceUnique";
3
+ export type { TError } from "./error";
4
+ export type { ExtractTuple } from "./extractTuple";
5
+ export type { TInArray } from "./inArray";
6
+ export type { MergeTuple } from "./mergeTuple";
7
+ export type { TNonEmptyArray } from "./noneEmptyArray";
8
+ export type { TPartialTuple } from "./partialTuple";
9
+ export type { TPartialTupleUnordered as TOptionalTupleUnordered } from "./partialTupleUnordered";
10
+ export type { TOptionalTupleUnorderedNonEmpty } from "./partialTupleUnorderedNonEmpty";
11
+ export type { TPartialTupleNonEmpty } from "./partialTurpleNonEmpty";
12
+ export type { TPermutationTuple } from "./permutationTuple";
13
+ export type { TShuffleTuple } from "./shuffleTuple";
14
+ export type { TStrictPartial } from "./strictPartial";
@@ -0,0 +1,3 @@
1
+ export type MergeTuple<T extends readonly any[], U> = {
2
+ [K in keyof T]: T[K] & U;
3
+ };
@@ -0,0 +1 @@
1
+ export type TNonEmptyArray<T> = readonly [T, ...T[]];
@@ -0,0 +1,3 @@
1
+ export type TPartialTuple<T extends readonly any[]> = T extends readonly [infer Head, ...infer Tail]
2
+ ? TPartialTuple<Tail> | [Head, ...TPartialTuple<Tail>]
3
+ : [];
@@ -0,0 +1,8 @@
1
+ import type { TPartialTuple } from "./partialTuple";
2
+ import type { TPermutationTuple } from "./permutationTuple";
3
+
4
+ export type TPartialTupleUnordered<T extends readonly any[]> = TPartialTuple<T> extends infer S
5
+ ? S extends readonly any[]
6
+ ? TPermutationTuple<S[number]>
7
+ : never
8
+ : never;
@@ -0,0 +1,9 @@
1
+ import type { TPartialTupleNonEmpty } from "./partialTurpleNonEmpty";
2
+ import type { TPermutationTuple } from "./permutationTuple";
3
+
4
+ export type TOptionalTupleUnorderedNonEmpty<T extends readonly any[]> =
5
+ TPartialTupleNonEmpty<T> extends infer S
6
+ ? S extends readonly any[]
7
+ ? TPermutationTuple<S[number]>
8
+ : never
9
+ : never;
@@ -0,0 +1,8 @@
1
+ import type { TPartialTuple } from "./partialTuple";
2
+
3
+ export type TPartialTupleNonEmpty<T extends readonly any[]> = T extends readonly [
4
+ infer Head,
5
+ ...infer Tail
6
+ ]
7
+ ? [Head, ...TPartialTuple<Tail>] | TPartialTupleNonEmpty<Tail>
8
+ : never;
@@ -0,0 +1,5 @@
1
+ export type TPermutationTuple<T, K = T> = [T] extends [never]
2
+ ? []
3
+ : T extends any
4
+ ? [T, ...TPermutationTuple<Exclude<K, T>>]
5
+ : never;
@@ -0,0 +1,7 @@
1
+ type Values<T> = T[keyof T];
2
+
3
+ export type TShuffleTuple<U extends string | number> = [U] extends [never]
4
+ ? []
5
+ : Values<{
6
+ [K in U]: [K, ...TShuffleTuple<Exclude<U, K>>];
7
+ }>;