@typia/utils 12.0.0-dev.20260309 → 12.0.0-dev.20260311

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 (103) hide show
  1. package/lib/converters/LlmSchemaConverter.d.ts +0 -1
  2. package/lib/converters/LlmSchemaConverter.js +4 -31
  3. package/lib/converters/LlmSchemaConverter.js.map +1 -1
  4. package/lib/converters/LlmSchemaConverter.mjs +2 -32
  5. package/lib/converters/LlmSchemaConverter.mjs.map +1 -1
  6. package/lib/http/HttpLlm.js +4 -5
  7. package/lib/http/HttpLlm.js.map +1 -1
  8. package/lib/http/HttpLlm.mjs +0 -1
  9. package/lib/http/HttpLlm.mjs.map +1 -1
  10. package/lib/http/internal/HttpLlmApplicationComposer.js +3 -4
  11. package/lib/http/internal/HttpLlmApplicationComposer.js.map +1 -1
  12. package/lib/http/internal/HttpLlmApplicationComposer.mjs +5 -2
  13. package/lib/http/internal/HttpLlmApplicationComposer.mjs.map +1 -1
  14. package/lib/index.mjs +9 -9
  15. package/lib/utils/LlmJson.mjs +9 -2
  16. package/lib/utils/LlmJson.mjs.map +1 -1
  17. package/lib/utils/internal/stringifyValidationFailure.js +17 -15
  18. package/lib/utils/internal/stringifyValidationFailure.js.map +1 -1
  19. package/lib/utils/internal/stringifyValidationFailure.mjs +17 -15
  20. package/lib/utils/internal/stringifyValidationFailure.mjs.map +1 -1
  21. package/lib/validators/internal/OpenApiOneOfValidator.mjs +5 -1
  22. package/lib/validators/internal/OpenApiOneOfValidator.mjs.map +1 -1
  23. package/package.json +2 -2
  24. package/src/converters/LlmSchemaConverter.ts +617 -647
  25. package/src/converters/OpenApiConverter.ts +285 -285
  26. package/src/converters/index.ts +5 -5
  27. package/src/converters/internal/LlmDescriptionInverter.ts +178 -178
  28. package/src/converters/internal/LlmParametersComposer.ts +52 -52
  29. package/src/converters/internal/OpenApiConstraintShifter.ts +154 -154
  30. package/src/converters/internal/OpenApiExclusiveEmender.ts +46 -46
  31. package/src/converters/internal/OpenApiV3Downgrader.ts +355 -355
  32. package/src/converters/internal/OpenApiV3Upgrader.ts +470 -470
  33. package/src/converters/internal/OpenApiV3_1Upgrader.ts +685 -685
  34. package/src/converters/internal/SwaggerV2Downgrader.ts +424 -424
  35. package/src/converters/internal/SwaggerV2Upgrader.ts +523 -523
  36. package/src/http/HttpError.ts +107 -107
  37. package/src/http/HttpLlm.ts +166 -167
  38. package/src/http/HttpMigration.ts +92 -92
  39. package/src/http/index.ts +3 -3
  40. package/src/http/internal/HttpLlmApplicationComposer.ts +360 -361
  41. package/src/http/internal/HttpLlmFunctionFetcher.ts +37 -37
  42. package/src/http/internal/HttpMigrateApplicationComposer.ts +56 -56
  43. package/src/http/internal/HttpMigrateRouteAccessor.ts +135 -135
  44. package/src/http/internal/HttpMigrateRouteComposer.ts +505 -505
  45. package/src/http/internal/HttpMigrateRouteFetcher.ts +203 -203
  46. package/src/index.ts +4 -4
  47. package/src/utils/ArrayUtil.ts +42 -42
  48. package/src/utils/LlmJson.ts +141 -141
  49. package/src/utils/MapUtil.ts +15 -15
  50. package/src/utils/NamingConvention.ts +205 -205
  51. package/src/utils/Singleton.ts +17 -17
  52. package/src/utils/StringUtil.ts +14 -14
  53. package/src/utils/dedent.ts +57 -57
  54. package/src/utils/index.ts +8 -8
  55. package/src/utils/internal/EndpointUtil.ts +44 -44
  56. package/src/utils/internal/JsonDescriptor.ts +70 -70
  57. package/src/utils/internal/OpenApiTypeCheckerBase.ts +822 -822
  58. package/src/utils/internal/coerceLlmArguments.ts +314 -314
  59. package/src/utils/internal/parseLenientJson.ts +894 -894
  60. package/src/utils/internal/stringifyValidationFailure.ts +415 -411
  61. package/src/validators/LlmTypeChecker.ts +402 -402
  62. package/src/validators/OpenApiTypeChecker.ts +297 -297
  63. package/src/validators/OpenApiV3TypeChecker.ts +70 -70
  64. package/src/validators/OpenApiV3_1TypeChecker.ts +86 -86
  65. package/src/validators/OpenApiValidator.ts +94 -94
  66. package/src/validators/SwaggerV2TypeChecker.ts +71 -71
  67. package/src/validators/functional/_isBigintString.ts +8 -8
  68. package/src/validators/functional/_isFormatByte.ts +7 -7
  69. package/src/validators/functional/_isFormatDate.ts +3 -3
  70. package/src/validators/functional/_isFormatDateTime.ts +4 -4
  71. package/src/validators/functional/_isFormatDuration.ts +4 -4
  72. package/src/validators/functional/_isFormatEmail.ts +4 -4
  73. package/src/validators/functional/_isFormatHostname.ts +4 -4
  74. package/src/validators/functional/_isFormatIdnEmail.ts +4 -4
  75. package/src/validators/functional/_isFormatIdnHostname.ts +4 -4
  76. package/src/validators/functional/_isFormatIpv4.ts +4 -4
  77. package/src/validators/functional/_isFormatIpv6.ts +4 -4
  78. package/src/validators/functional/_isFormatIri.ts +3 -3
  79. package/src/validators/functional/_isFormatIriReference.ts +4 -4
  80. package/src/validators/functional/_isFormatJsonPointer.ts +3 -3
  81. package/src/validators/functional/_isFormatPassword.ts +1 -1
  82. package/src/validators/functional/_isFormatRegex.ts +8 -8
  83. package/src/validators/functional/_isFormatRelativeJsonPointer.ts +4 -4
  84. package/src/validators/functional/_isFormatTime.ts +4 -4
  85. package/src/validators/functional/_isFormatUri.ts +6 -6
  86. package/src/validators/functional/_isFormatUriReference.ts +5 -5
  87. package/src/validators/functional/_isFormatUriTemplate.ts +4 -4
  88. package/src/validators/functional/_isFormatUrl.ts +4 -4
  89. package/src/validators/functional/_isFormatUuid.ts +3 -3
  90. package/src/validators/functional/_isUniqueItems.ts +159 -159
  91. package/src/validators/index.ts +14 -14
  92. package/src/validators/internal/IOpenApiValidatorContext.ts +17 -17
  93. package/src/validators/internal/OpenApiArrayValidator.ts +49 -49
  94. package/src/validators/internal/OpenApiBooleanValidator.ts +11 -11
  95. package/src/validators/internal/OpenApiConstantValidator.ts +11 -11
  96. package/src/validators/internal/OpenApiIntegerValidator.ts +49 -49
  97. package/src/validators/internal/OpenApiNumberValidator.ts +48 -48
  98. package/src/validators/internal/OpenApiObjectValidator.ts +83 -83
  99. package/src/validators/internal/OpenApiOneOfValidator.ts +309 -309
  100. package/src/validators/internal/OpenApiSchemaNamingRule.ts +124 -124
  101. package/src/validators/internal/OpenApiStationValidator.ts +115 -115
  102. package/src/validators/internal/OpenApiStringValidator.ts +88 -88
  103. package/src/validators/internal/OpenApiTupleValidator.ts +55 -55
@@ -1,203 +1,203 @@
1
- import { IHttpConnection, IHttpResponse } from "@typia/interface";
2
-
3
- import { HttpError } from "../HttpError";
4
- import type { HttpMigration } from "../HttpMigration";
5
-
6
- export namespace HttpMigrateRouteFetcher {
7
- export const execute = async (
8
- props: HttpMigration.IFetchProps,
9
- ): Promise<unknown> => {
10
- const result: IHttpResponse = await _Propagate("request", props);
11
- props.route.success?.media;
12
- if (result.status !== 200 && result.status !== 201)
13
- throw new HttpError(
14
- props.route.method.toUpperCase() as "GET",
15
- props.route.path,
16
- result.status,
17
- result.headers,
18
- result.body as string,
19
- );
20
- return result.body;
21
- };
22
-
23
- export const propagate = (
24
- props: HttpMigration.IFetchProps,
25
- ): Promise<IHttpResponse> => _Propagate("propagate", props);
26
- }
27
-
28
- const _Propagate = async (
29
- from: string,
30
- props: HttpMigration.IFetchProps,
31
- ): Promise<IHttpResponse> => {
32
- // VALIDATE PARAMETERS
33
- const error = (message: string) =>
34
- new Error(`Error on MigrateRouteFetcher.${from}(): ${message}`);
35
- if (Array.isArray(props.parameters)) {
36
- if (props.route.parameters.length !== props.parameters.length)
37
- throw error(`number of parameters is not matched.`);
38
- } else if (
39
- props.route.parameters.every(
40
- (p) => (props.parameters as Record<string, any>)[p.key] !== undefined,
41
- ) === false
42
- )
43
- throw error(`number of parameters is not matched.`);
44
-
45
- // VALIDATE QUERY
46
- if (!!props.route.query !== !!props.query)
47
- throw error(`query is not matched.`);
48
- else if (!!props.route.body !== (props.body !== undefined))
49
- throw error(`body is not matched.`);
50
-
51
- // INIT REQUEST DATA
52
- const headers: Record<string, IHttpConnection.HeaderValue | undefined> = {
53
- ...(props.connection.headers ?? {}),
54
- ...(props.route.body?.type &&
55
- props.route.body.type !== "multipart/form-data"
56
- ? { "Content-Type": props.route.body.type }
57
- : {}),
58
- };
59
- const init: RequestInit = {
60
- ...(props.connection.options ?? {}),
61
- method: props.route.method.toUpperCase(),
62
- headers: (() => {
63
- const output: [string, string][] = [];
64
- for (const [key, value] of Object.entries(headers))
65
- if (value === undefined) continue;
66
- else if (Array.isArray(value))
67
- for (const v of value) output.push([key, String(v)]);
68
- else output.push([key, String(value)]);
69
- return output;
70
- })(),
71
- };
72
- if (props.body !== undefined)
73
- init.body = (
74
- props.route.body?.type === "application/x-www-form-urlencoded"
75
- ? requestQueryBody(props.body)
76
- : props.route.body?.type === "multipart/form-data"
77
- ? requestFormDataBody(props.body)
78
- : props.route.body?.type === "application/json"
79
- ? JSON.stringify(props.body)
80
- : props.body
81
- ) as any;
82
-
83
- // DO REQUEST
84
- const resolvedPath: string =
85
- props.connection.host.endsWith("/") === false &&
86
- props.route.emendedPath.startsWith("/") === false
87
- ? `/${getPath(props)}`
88
- : getPath(props);
89
- const url: URL = new URL(`${props.connection.host}${resolvedPath}`);
90
-
91
- const response: Response = await (props.connection.fetch ?? fetch)(url, init);
92
- const status: number = response.status;
93
- const out = (body: unknown): IHttpResponse => ({
94
- status,
95
- headers: responseHeaders(response.headers),
96
- body,
97
- });
98
-
99
- if (status === 200 || status === 201) {
100
- // SUCCESS CASE
101
- if (props.route.method.toUpperCase() === "HEAD") return out(undefined);
102
- else if (
103
- props.route.success === null ||
104
- props.route.success.type === "text/plain"
105
- )
106
- return out(await response.text());
107
- else if (props.route.success.type === "application/json") {
108
- const text: string = await response.text();
109
- return out(text.length ? JSON.parse(text) : undefined);
110
- } else if (props.route.success.type === "application/x-www-form-urlencoded")
111
- return out(new URLSearchParams(await response.text()));
112
- else if (props.route.success.type === "multipart/form-data")
113
- return out(await response.formData());
114
- throw error("Unsupported response body type.");
115
- } else {
116
- // FAILURE CASE
117
- const type: string = (
118
- response.headers.get("content-type") ??
119
- response.headers.get("Content-Type") ??
120
- ""
121
- )
122
- .split(";")[0]!
123
- .trim();
124
- if (type === "" || type.startsWith("text/"))
125
- return out(await response.text());
126
- else if (type === "application/json") return out(await response.json());
127
- else if (type === "application/x-www-form-urlencoded")
128
- return out(new URLSearchParams(await response.text()));
129
- else if (type === "multipart/form-data")
130
- return out(await response.formData());
131
- else if (type === "application/octet-stream")
132
- return out(await response.blob());
133
- return out(await response.text());
134
- }
135
- };
136
-
137
- const getPath = (
138
- props: Pick<HttpMigration.IFetchProps, "route" | "parameters" | "query">,
139
- ): string => {
140
- let path: string = props.route.emendedPath;
141
- props.route.parameters.forEach((p, i) => {
142
- path = path.replace(
143
- `:${p.key}`,
144
- encodeURIComponent(
145
- String(
146
- (Array.isArray(props.parameters)
147
- ? props.parameters[i]
148
- : props.parameters[p.key]) ?? "null",
149
- ),
150
- ),
151
- );
152
- });
153
- if (props.route.query) path += getQueryPath(props.query ?? {});
154
- return path;
155
- };
156
-
157
- const getQueryPath = (query: Record<string, any>): string => {
158
- const variables = new URLSearchParams();
159
- for (const [key, value] of Object.entries(query))
160
- if (undefined === value) continue;
161
- else if (Array.isArray(value))
162
- value.forEach((elem: any) => variables.append(key, String(elem)));
163
- else variables.set(key, String(value));
164
- return 0 === variables.size ? "" : `?${variables.toString()}`;
165
- };
166
-
167
- const requestQueryBody = (input: any): URLSearchParams => {
168
- const q: URLSearchParams = new URLSearchParams();
169
- for (const [key, value] of Object.entries(input))
170
- if (value === undefined) continue;
171
- else if (Array.isArray(value))
172
- value.forEach((elem) => q.append(key, String(elem)));
173
- else q.set(key, String(value));
174
- return q;
175
- };
176
- const requestFormDataBody = (input: Record<string, any>): FormData => {
177
- const encoded: FormData = new FormData();
178
- const append = (key: string) => (value: any) => {
179
- if (value === undefined) return;
180
- else if (typeof File === "function" && value instanceof File)
181
- encoded.append(key, value, value.name);
182
- else encoded.append(key, value);
183
- };
184
- for (const [key, value] of Object.entries(input))
185
- if (Array.isArray(value)) value.map(append(key));
186
- else append(key)(value);
187
- return encoded;
188
- };
189
-
190
- const responseHeaders = (
191
- headers: Headers,
192
- ): Record<string, string | string[]> => {
193
- const output: Record<string, string | string[]> = {};
194
- headers.forEach((value, key) => {
195
- if (key === "set-cookie") {
196
- output[key] ??= [];
197
- (output[key] as string[]).push(
198
- ...value.split(";").map((str) => str.trim()),
199
- );
200
- } else output[key] = value;
201
- });
202
- return output;
203
- };
1
+ import { IHttpConnection, IHttpResponse } from "@typia/interface";
2
+
3
+ import { HttpError } from "../HttpError";
4
+ import type { HttpMigration } from "../HttpMigration";
5
+
6
+ export namespace HttpMigrateRouteFetcher {
7
+ export const execute = async (
8
+ props: HttpMigration.IFetchProps,
9
+ ): Promise<unknown> => {
10
+ const result: IHttpResponse = await _Propagate("request", props);
11
+ props.route.success?.media;
12
+ if (result.status !== 200 && result.status !== 201)
13
+ throw new HttpError(
14
+ props.route.method.toUpperCase() as "GET",
15
+ props.route.path,
16
+ result.status,
17
+ result.headers,
18
+ result.body as string,
19
+ );
20
+ return result.body;
21
+ };
22
+
23
+ export const propagate = (
24
+ props: HttpMigration.IFetchProps,
25
+ ): Promise<IHttpResponse> => _Propagate("propagate", props);
26
+ }
27
+
28
+ const _Propagate = async (
29
+ from: string,
30
+ props: HttpMigration.IFetchProps,
31
+ ): Promise<IHttpResponse> => {
32
+ // VALIDATE PARAMETERS
33
+ const error = (message: string) =>
34
+ new Error(`Error on MigrateRouteFetcher.${from}(): ${message}`);
35
+ if (Array.isArray(props.parameters)) {
36
+ if (props.route.parameters.length !== props.parameters.length)
37
+ throw error(`number of parameters is not matched.`);
38
+ } else if (
39
+ props.route.parameters.every(
40
+ (p) => (props.parameters as Record<string, any>)[p.key] !== undefined,
41
+ ) === false
42
+ )
43
+ throw error(`number of parameters is not matched.`);
44
+
45
+ // VALIDATE QUERY
46
+ if (!!props.route.query !== !!props.query)
47
+ throw error(`query is not matched.`);
48
+ else if (!!props.route.body !== (props.body !== undefined))
49
+ throw error(`body is not matched.`);
50
+
51
+ // INIT REQUEST DATA
52
+ const headers: Record<string, IHttpConnection.HeaderValue | undefined> = {
53
+ ...(props.connection.headers ?? {}),
54
+ ...(props.route.body?.type &&
55
+ props.route.body.type !== "multipart/form-data"
56
+ ? { "Content-Type": props.route.body.type }
57
+ : {}),
58
+ };
59
+ const init: RequestInit = {
60
+ ...(props.connection.options ?? {}),
61
+ method: props.route.method.toUpperCase(),
62
+ headers: (() => {
63
+ const output: [string, string][] = [];
64
+ for (const [key, value] of Object.entries(headers))
65
+ if (value === undefined) continue;
66
+ else if (Array.isArray(value))
67
+ for (const v of value) output.push([key, String(v)]);
68
+ else output.push([key, String(value)]);
69
+ return output;
70
+ })(),
71
+ };
72
+ if (props.body !== undefined)
73
+ init.body = (
74
+ props.route.body?.type === "application/x-www-form-urlencoded"
75
+ ? requestQueryBody(props.body)
76
+ : props.route.body?.type === "multipart/form-data"
77
+ ? requestFormDataBody(props.body)
78
+ : props.route.body?.type === "application/json"
79
+ ? JSON.stringify(props.body)
80
+ : props.body
81
+ ) as any;
82
+
83
+ // DO REQUEST
84
+ const resolvedPath: string =
85
+ props.connection.host.endsWith("/") === false &&
86
+ props.route.emendedPath.startsWith("/") === false
87
+ ? `/${getPath(props)}`
88
+ : getPath(props);
89
+ const url: URL = new URL(`${props.connection.host}${resolvedPath}`);
90
+
91
+ const response: Response = await (props.connection.fetch ?? fetch)(url, init);
92
+ const status: number = response.status;
93
+ const out = (body: unknown): IHttpResponse => ({
94
+ status,
95
+ headers: responseHeaders(response.headers),
96
+ body,
97
+ });
98
+
99
+ if (status === 200 || status === 201) {
100
+ // SUCCESS CASE
101
+ if (props.route.method.toUpperCase() === "HEAD") return out(undefined);
102
+ else if (
103
+ props.route.success === null ||
104
+ props.route.success.type === "text/plain"
105
+ )
106
+ return out(await response.text());
107
+ else if (props.route.success.type === "application/json") {
108
+ const text: string = await response.text();
109
+ return out(text.length ? JSON.parse(text) : undefined);
110
+ } else if (props.route.success.type === "application/x-www-form-urlencoded")
111
+ return out(new URLSearchParams(await response.text()));
112
+ else if (props.route.success.type === "multipart/form-data")
113
+ return out(await response.formData());
114
+ throw error("Unsupported response body type.");
115
+ } else {
116
+ // FAILURE CASE
117
+ const type: string = (
118
+ response.headers.get("content-type") ??
119
+ response.headers.get("Content-Type") ??
120
+ ""
121
+ )
122
+ .split(";")[0]!
123
+ .trim();
124
+ if (type === "" || type.startsWith("text/"))
125
+ return out(await response.text());
126
+ else if (type === "application/json") return out(await response.json());
127
+ else if (type === "application/x-www-form-urlencoded")
128
+ return out(new URLSearchParams(await response.text()));
129
+ else if (type === "multipart/form-data")
130
+ return out(await response.formData());
131
+ else if (type === "application/octet-stream")
132
+ return out(await response.blob());
133
+ return out(await response.text());
134
+ }
135
+ };
136
+
137
+ const getPath = (
138
+ props: Pick<HttpMigration.IFetchProps, "route" | "parameters" | "query">,
139
+ ): string => {
140
+ let path: string = props.route.emendedPath;
141
+ props.route.parameters.forEach((p, i) => {
142
+ path = path.replace(
143
+ `:${p.key}`,
144
+ encodeURIComponent(
145
+ String(
146
+ (Array.isArray(props.parameters)
147
+ ? props.parameters[i]
148
+ : props.parameters[p.key]) ?? "null",
149
+ ),
150
+ ),
151
+ );
152
+ });
153
+ if (props.route.query) path += getQueryPath(props.query ?? {});
154
+ return path;
155
+ };
156
+
157
+ const getQueryPath = (query: Record<string, any>): string => {
158
+ const variables = new URLSearchParams();
159
+ for (const [key, value] of Object.entries(query))
160
+ if (undefined === value) continue;
161
+ else if (Array.isArray(value))
162
+ value.forEach((elem: any) => variables.append(key, String(elem)));
163
+ else variables.set(key, String(value));
164
+ return 0 === variables.size ? "" : `?${variables.toString()}`;
165
+ };
166
+
167
+ const requestQueryBody = (input: any): URLSearchParams => {
168
+ const q: URLSearchParams = new URLSearchParams();
169
+ for (const [key, value] of Object.entries(input))
170
+ if (value === undefined) continue;
171
+ else if (Array.isArray(value))
172
+ value.forEach((elem) => q.append(key, String(elem)));
173
+ else q.set(key, String(value));
174
+ return q;
175
+ };
176
+ const requestFormDataBody = (input: Record<string, any>): FormData => {
177
+ const encoded: FormData = new FormData();
178
+ const append = (key: string) => (value: any) => {
179
+ if (value === undefined) return;
180
+ else if (typeof File === "function" && value instanceof File)
181
+ encoded.append(key, value, value.name);
182
+ else encoded.append(key, value);
183
+ };
184
+ for (const [key, value] of Object.entries(input))
185
+ if (Array.isArray(value)) value.map(append(key));
186
+ else append(key)(value);
187
+ return encoded;
188
+ };
189
+
190
+ const responseHeaders = (
191
+ headers: Headers,
192
+ ): Record<string, string | string[]> => {
193
+ const output: Record<string, string | string[]> = {};
194
+ headers.forEach((value, key) => {
195
+ if (key === "set-cookie") {
196
+ output[key] ??= [];
197
+ (output[key] as string[]).push(
198
+ ...value.split(";").map((str) => str.trim()),
199
+ );
200
+ } else output[key] = value;
201
+ });
202
+ return output;
203
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./converters/index";
2
- export * from "./http/index";
3
- export * from "./utils/index";
4
- export * from "./validators/index";
1
+ export * from "./converters/index";
2
+ export * from "./http/index";
3
+ export * from "./utils/index";
4
+ export * from "./validators/index";
@@ -1,42 +1,42 @@
1
- /** @internal */
2
- export namespace ArrayUtil {
3
- export const has = <T>(array: T[], pred: (elem: T) => boolean): boolean =>
4
- array.some(pred);
5
-
6
- export const add = <T>(
7
- array: T[],
8
- value: T,
9
- pred: (x: T, y: T) => boolean = (x, y) => x === y,
10
- ): boolean => {
11
- if (array.some((elem) => pred(elem, value))) return false;
12
- array.push(value);
13
- return true;
14
- };
15
-
16
- export const set = <Key, T>(
17
- array: T[],
18
- value: T,
19
- key: (elem: T) => Key,
20
- ): void => {
21
- if (array.some((elem) => key(elem) === key(value))) return;
22
- array.push(value);
23
- };
24
-
25
- export const take = <T>(
26
- array: T[],
27
- pred: (elem: T) => boolean,
28
- init: () => T,
29
- ): T => {
30
- const index: number = array.findIndex(pred);
31
- if (index !== -1) return array[index]!;
32
-
33
- const elem: T = init();
34
- array.push(elem);
35
- return elem;
36
- };
37
-
38
- export const repeat = <T>(
39
- count: number,
40
- closure: (index: number, count: number) => T,
41
- ): T[] => new Array(count).fill("").map((_, index) => closure(index, count));
42
- }
1
+ /** @internal */
2
+ export namespace ArrayUtil {
3
+ export const has = <T>(array: T[], pred: (elem: T) => boolean): boolean =>
4
+ array.some(pred);
5
+
6
+ export const add = <T>(
7
+ array: T[],
8
+ value: T,
9
+ pred: (x: T, y: T) => boolean = (x, y) => x === y,
10
+ ): boolean => {
11
+ if (array.some((elem) => pred(elem, value))) return false;
12
+ array.push(value);
13
+ return true;
14
+ };
15
+
16
+ export const set = <Key, T>(
17
+ array: T[],
18
+ value: T,
19
+ key: (elem: T) => Key,
20
+ ): void => {
21
+ if (array.some((elem) => key(elem) === key(value))) return;
22
+ array.push(value);
23
+ };
24
+
25
+ export const take = <T>(
26
+ array: T[],
27
+ pred: (elem: T) => boolean,
28
+ init: () => T,
29
+ ): T => {
30
+ const index: number = array.findIndex(pred);
31
+ if (index !== -1) return array[index]!;
32
+
33
+ const elem: T = init();
34
+ array.push(elem);
35
+ return elem;
36
+ };
37
+
38
+ export const repeat = <T>(
39
+ count: number,
40
+ closure: (index: number, count: number) => T,
41
+ ): T[] => new Array(count).fill("").map((_, index) => closure(index, count));
42
+ }