utilium 2.0.0-pre.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/struct.ts DELETED
@@ -1,290 +0,0 @@
1
- import { BufferViewArray } from './buffer.js';
2
- import { _debugLog } from './debugging.js';
3
- import * as primitive from './internal/primitives.js';
4
- import type {
5
- DecoratorContext,
6
- Instance,
7
- InstanceLike,
8
- Member,
9
- Metadata,
10
- Options,
11
- Size,
12
- StaticLike,
13
- TypeLike,
14
- } from './internal/struct.js';
15
- import { checkStruct, initMetadata, isInstance, isStatic, isStruct } from './internal/struct.js';
16
- import { _throw } from './misc.js';
17
- import { getAllPrototypes } from './objects.js';
18
- export * as Struct from './internal/struct.js';
19
-
20
- /**
21
- * Gets the size in bytes of a type
22
- */
23
- export function sizeof<T extends TypeLike>(type: T | T[]): Size<T> {
24
- if (type === undefined || type === null) return 0 as Size<T>;
25
-
26
- if (Array.isArray(type)) {
27
- let size = 0;
28
-
29
- for (let i = 0; i < type.length; i++) {
30
- size += sizeof(type[i]);
31
- }
32
-
33
- return size as Size<T>;
34
- }
35
-
36
- // primitive or character
37
- if (typeof type == 'string') {
38
- primitive.checkValid(type);
39
-
40
- return primitive.types[primitive.normalize(type)].size as Size<T>;
41
- }
42
-
43
- if (primitive.isType(type)) return type.size as Size<T>;
44
-
45
- checkStruct(type);
46
-
47
- const constructor = isStatic(type) ? type : type.constructor;
48
- const { struct, structInit } = constructor[Symbol.metadata];
49
-
50
- if (isStatic(type) || !struct.isDynamic) return struct.staticSize as Size<T>;
51
-
52
- const last = structInit.members.at(-1)!;
53
- const length = (type as any)[last.length as keyof typeof type];
54
- let dynamicSize = 0;
55
-
56
- if (primitive.isType(last.type)) {
57
- dynamicSize = last.type.size * length;
58
- } else {
59
- const value = type[last.name];
60
-
61
- for (let i = 0; i < length; i++) {
62
- dynamicSize += sizeof(isStruct(value[i]) ? value[i] : last.type);
63
- }
64
- }
65
-
66
- return (struct.isUnion ? Math.max(struct.staticSize, dynamicSize) : struct.staticSize + dynamicSize) as Size<T>;
67
- }
68
-
69
- /**
70
- * Returns the offset (in bytes) of a member in a struct.
71
- */
72
- export function offsetof(type: StaticLike | InstanceLike, memberName: string): number {
73
- checkStruct(type);
74
-
75
- const constructor = isStatic(type) ? type : type.constructor;
76
-
77
- const member = constructor[Symbol.metadata].struct.members.get(memberName);
78
-
79
- if (!member) throw new Error('Struct does not have member: ' + memberName);
80
-
81
- return member.offset;
82
- }
83
-
84
- /** Aligns a number */
85
- export function align(value: number, alignment: number): number {
86
- return Math.ceil(value / alignment) * alignment;
87
- }
88
-
89
- /**
90
- * Decorates a class as a struct
91
- */
92
- export function struct(options: Partial<Options> = {}) {
93
- return function _decorateStruct<T extends StaticLike>(
94
- target: T,
95
- context: ClassDecoratorContext<T> & DecoratorContext
96
- ): void {
97
- const init = initMetadata(context);
98
-
99
- const members = new Map<string, Member>();
100
-
101
- for (const member of init.members) {
102
- if (options.isUnion) member.offset = 0;
103
-
104
- _debugLog('define', target.name + '.' + member.name);
105
-
106
- members.set(member.name, member);
107
- }
108
-
109
- context.metadata.struct = {
110
- options,
111
- members,
112
- staticSize: init.size,
113
- isDynamic: init.isDynamic,
114
- isUnion: options.isUnion ?? false,
115
- } satisfies Metadata;
116
- };
117
- }
118
-
119
- export interface MemberOptions {
120
- bigEndian?: boolean;
121
- length?: number | string;
122
- align?: number;
123
- typeName?: string;
124
- }
125
-
126
- /**
127
- * Decorates a class member to be serialized
128
- */
129
- export function member<V>(type: primitive.Type | StaticLike, opt: MemberOptions = {}) {
130
- return function __decorateMember(value: Target<V>, context: Context<V>): Result<V> {
131
- if (context.kind != 'accessor') throw new Error('Member must be an accessor');
132
-
133
- const init = initMetadata(context);
134
-
135
- if (init.isDynamic) throw new Error('Dynamic members must be declared at the end of the struct');
136
-
137
- let name = context.name;
138
- if (typeof name == 'symbol') {
139
- console.warn('Symbol used for struct member name will be coerced to string: ' + name.toString());
140
- name = name.toString();
141
- }
142
-
143
- if (!name) throw new ReferenceError('Invalid name for struct member');
144
-
145
- if (!primitive.isType(type) && !isStatic(type)) throw new TypeError('Not a valid type: ' + type.name);
146
-
147
- if (typeof opt.length == 'string') {
148
- const countedBy = init.members.find(m => m.name == opt.length);
149
-
150
- if (!countedBy) throw new Error(`"${opt.length}" is not declared and cannot be used to count "${name}"`);
151
-
152
- if (!primitive.isType(countedBy.type))
153
- throw new Error(`"${opt.length}" is not a number and cannot be used to count "${name}"`);
154
-
155
- init.isDynamic = true;
156
- }
157
-
158
- const size = align(
159
- sizeof(type) * (typeof opt.length == 'string' ? 0 : (opt.length ?? 1)),
160
- opt.align ?? sizeof(type)
161
- );
162
-
163
- const member = {
164
- name,
165
- offset: init.size,
166
- type,
167
- length: opt.length,
168
- size,
169
- decl: `${opt.typeName ?? type.name} ${name}${opt.length !== undefined ? `[${JSON.stringify(opt.length)}]` : ''}`,
170
- littleEndian: !opt.bigEndian,
171
- } satisfies Member;
172
-
173
- init.members.push(member);
174
-
175
- // Apply after setting `offset`
176
- init.size += size;
177
-
178
- return {
179
- get() {
180
- return _get(this, member);
181
- },
182
- set(value) {
183
- _set(this, member, value);
184
- },
185
- };
186
- };
187
- }
188
-
189
- /** Gets the length of a member */
190
- function _memberLength<T extends Metadata>(instance: Instance<T>, length?: number | string): number {
191
- if (length === undefined) return -1;
192
- if (typeof length == 'string') return instance[length];
193
- return Number.isSafeInteger(length) && length >= 0
194
- ? length
195
- : _throw(new Error('Array lengths must be natural numbers'));
196
- }
197
-
198
- function _set(instance: Instance, member: Member, value: any, index?: number) {
199
- const { name, type, length: rawLength } = member;
200
- const length = _memberLength(instance, rawLength);
201
-
202
- if (!primitive.isType(type)) {
203
- if (!isInstance(value)) return _debugLog(`Tried to set "${name}" to a non-instance value`);
204
-
205
- if (length > 0 && typeof index != 'number') {
206
- for (let i = 0; i < length; i++) _set(instance, member, value[i], i);
207
- return;
208
- }
209
-
210
- if (!Array.from(getAllPrototypes(value.constructor)).some(c => c === type))
211
- throw new Error(`${value.constructor.name} is not a subtype of ${type.name}`);
212
-
213
- const offset = instance.byteOffset + member.offset + (index ?? 0) * sizeof(type);
214
-
215
- // It's already the same value
216
- if (value.buffer === instance.buffer && value.byteOffset === offset) return;
217
-
218
- const current = new Uint8Array(instance.buffer, offset, sizeof(value));
219
-
220
- current.set(new Uint8Array(value.buffer, value.byteOffset, sizeof(value)));
221
-
222
- return;
223
- }
224
-
225
- const view = new DataView(instance.buffer, instance.byteOffset, instance.byteLength);
226
-
227
- if (length > 0 && typeof index != 'number') {
228
- for (let i = 0; i < length; i++) {
229
- const offset = member.offset + i * type.size;
230
- type.set(view, offset, member.littleEndian, value[i]);
231
- }
232
- return;
233
- }
234
-
235
- if (typeof value == 'string') value = value.charCodeAt(0);
236
-
237
- type.set(view, member.offset + (index ?? 0) * type.size, member.littleEndian, value);
238
- }
239
-
240
- function _get(instance: Instance, member: Member, index?: number) {
241
- const { type, length: rawLength } = member;
242
- const length = _memberLength(instance, rawLength);
243
-
244
- if (length > 0 && typeof index != 'number') {
245
- return new (primitive.isType(type) ? type.array : BufferViewArray(type, sizeof(type)))(
246
- instance.buffer,
247
- instance.byteOffset + member.offset,
248
- length * sizeof(type)
249
- );
250
- }
251
-
252
- const offset = member.offset + (index ?? 0) * sizeof(type);
253
-
254
- if (isStatic(type)) return new type(instance.buffer, offset, sizeof(type));
255
-
256
- const view = new DataView(instance.buffer, instance.byteOffset, instance.byteLength);
257
- return type.get(view, offset, member.littleEndian);
258
- }
259
-
260
- // Decorator utility types
261
- type Target<V> = ClassAccessorDecoratorTarget<any, V>;
262
- type Result<V> = ClassAccessorDecoratorResult<any, V>;
263
- type Context<V> = ClassAccessorDecoratorContext<any, V> & DecoratorContext;
264
- type Decorator<V> = (value: Target<V>, context: Context<V>) => Result<V>;
265
-
266
- function _member<T extends primitive.Valid>(typeName: T) {
267
- const type = primitive.types[primitive.normalize(typeName)];
268
-
269
- function _structMemberDecorator<V>(length: number | string): Decorator<V>;
270
- function _structMemberDecorator<V>(value: Target<V>, context: Context<V>): Result<V>;
271
- function _structMemberDecorator<V>(
272
- valueOrLength: Target<V> | number | string,
273
- context?: Context<V>
274
- ): Decorator<V> | Result<V> {
275
- return typeof valueOrLength == 'number' || typeof valueOrLength == 'string'
276
- ? member<V>(type, { length: valueOrLength, typeName })
277
- : member<V>(type, { typeName })(valueOrLength, context!);
278
- }
279
-
280
- return _structMemberDecorator;
281
- }
282
-
283
- /**
284
- * Shortcut types
285
- *
286
- * Instead of writing `@member(type)` you can write `@types.type`, or `@types.type(length)` for arrays
287
- */
288
- export const types = Object.fromEntries(primitive.validNames.map(t => [t, _member(t)])) as {
289
- [K in primitive.Valid]: ReturnType<typeof _member<K>>;
290
- };
package/src/types.ts DELETED
@@ -1,277 +0,0 @@
1
- /**
2
- * Expands the type T (for intellisense and debugging)
3
- * @see https://stackoverflow.com/a/69288824/17637456
4
- */
5
- export type Expand<T> = T extends (...args: infer A) => infer R
6
- ? (...args: Expand<A>) => Expand<R>
7
- : T extends infer O
8
- ? { [K in keyof O]: O[K] }
9
- : never;
10
-
11
- /**
12
- * Recursivly expands the type T (for intellisense and debugging)
13
- * @see https://stackoverflow.com/a/69288824/17637456
14
- */
15
- export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
16
- ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
17
- : T extends object
18
- ? T extends infer O
19
- ? { [K in keyof O]: ExpandRecursively<O[K]> }
20
- : never
21
- : T;
22
-
23
- /**
24
- * Extracts an object with properties assignable to P from an object T
25
- * @see https://stackoverflow.com/a/71532723/17637456
26
- */
27
- export type ExtractProperties<T, P> = {
28
- [K in keyof T as T[K] extends infer Prop ? (Prop extends P ? K : never) : never]: T[K];
29
- };
30
-
31
- /**
32
- * Extract the keys of T which are required
33
- * @see https://stackoverflow.com/a/55247867/17637456
34
- */
35
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
36
- export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K }[keyof T];
37
-
38
- /**
39
- * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
40
- */
41
- export type RequiredProperties<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
42
-
43
- /**
44
- * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
45
- */
46
- export type DeepRequired<T> = {
47
- [K in keyof T]: Required<DeepRequired<T[K]>>;
48
- };
49
-
50
- /**
51
- * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
52
- */
53
- export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
54
-
55
- /**
56
- * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
57
- */
58
- export type NestedKeys<T extends object> = {
59
- [P in keyof T & (string | number)]: T[P] extends Date
60
- ? `${P}`
61
- : T[P] extends Record<string, unknown>
62
- ? `${P}` | `${P}.${NestedKeys<T[P]>}`
63
- : `${P}`;
64
- }[keyof T & (string | number)];
65
-
66
- /**
67
- * @see https://dev.to/tmhao2005/ts-useful-advanced-types-3k5e
68
- */
69
- export type PartialRecursive<T> = {
70
- [P in keyof T]?: T[P] extends (infer U)[]
71
- ? PartialRecursive<U>[]
72
- : T[P] extends object | undefined
73
- ? PartialRecursive<T[P]>
74
- : T[P];
75
- };
76
-
77
- /**
78
- * Get the keys of a union of objects
79
- * @see https://stackoverflow.com/a/65805753/17637456
80
- */
81
- export type UnionKeys<T> = T extends T ? keyof T : never;
82
-
83
- type StrictUnionHelper<T, TAll> = T extends unknown
84
- ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>>
85
- : never;
86
-
87
- /**
88
- * @see https://stackoverflow.com/a/65805753/17637456
89
- */
90
- export type StrictUnion<T> = Expand<StrictUnionHelper<T, T>>;
91
-
92
- // Tuple and array manipulation
93
-
94
- /**
95
- * Empty
96
- */
97
- export type Empty = [];
98
-
99
- /**
100
- * Removes the first element of T and shifts
101
- */
102
- export type Shift<T extends unknown[]> = T extends [unknown, ...infer Rest] ? Rest : never;
103
-
104
- /**
105
- * Gets the first element of T
106
- */
107
- export type First<T extends unknown[]> = T extends [infer F, ...unknown[]] ? F : never;
108
-
109
- /**
110
- * Inserts V into T at the start of T
111
- */
112
- export type Unshift<T extends unknown[], V> = [V, ...T];
113
-
114
- /**
115
- * Removes the last element of T
116
- */
117
- export type Pop<T extends unknown[]> = T extends [...infer _, unknown] ? _ : never;
118
-
119
- /**
120
- * Gets the last element of T
121
- */
122
- export type Last<T extends unknown[]> = T extends [...unknown[], infer Last] ? Last : never;
123
-
124
- /**
125
- * Appends V to T
126
- */
127
- export type Push<T extends unknown[], V> = [...T, V];
128
-
129
- /**
130
- * Concats A and B
131
- */
132
- export type Concat<A extends unknown[], B extends unknown[]> = Empty extends B ? A : Concat<Unshift<A, 0>, Shift<B>>;
133
-
134
- /**
135
- * Extracts from A what is not B
136
- *
137
- * @remarks
138
- * It does not remove duplicates (so Remove\<[0, 0, 0], [0, 0]\> yields [0]). This is intended and necessary behavior.
139
- */
140
- export type Remove<A extends unknown[], B extends unknown[]> = Empty extends B ? A : Remove<Shift<A>, Shift<B>>;
141
-
142
- /**
143
- * The length of T
144
- */
145
- export type Length<T extends { length: number }> = T['length'];
146
-
147
- type _FromLength<N extends number, R extends unknown[] = Empty> =
148
- Length<R> extends N ? R : _FromLength<N, Unshift<R, 0>>;
149
-
150
- /**
151
- * Creates a tuple of length N
152
- */
153
- export type FromLength<N extends number> = _FromLength<N>;
154
-
155
- // compile-time math
156
-
157
- /**
158
- * Increments N
159
- */
160
- export type Increment<N extends number> = Length<Unshift<_FromLength<N>, 0>>;
161
-
162
- /**
163
- * Decrements N
164
- */
165
- export type Decrement<N extends number> = Length<Shift<_FromLength<N>>>;
166
-
167
- /**
168
- * Gets the sum of A and B
169
- */
170
- export type Add<A extends number, B extends number> = Length<Concat<_FromLength<A>, _FromLength<B>>>;
171
-
172
- /**
173
- * Subtracts B from A
174
- */
175
- export type Subtract<A extends number, B extends number> = Length<Remove<_FromLength<A>, _FromLength<B>>>;
176
-
177
- /**
178
- * Gets the type of an array's members
179
- */
180
- export type Member<T, D = null> = D extends 0
181
- ? T
182
- : T extends (infer U)[]
183
- ? Member<U, D extends number ? Decrement<D> : null>
184
- : T;
185
-
186
- /**
187
- * Flattens an array
188
- */
189
- export type FlattenArray<A extends unknown[], D = null> = A extends (infer U)[]
190
- ? Member<Exclude<U, A>, D>[]
191
- : A extends unknown[]
192
- ? { [K in keyof A]: Member<A[K], D> }
193
- : A;
194
-
195
- /**
196
- * Whether T is a tuple
197
- */
198
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
199
- export type IsTuple<T> = T extends [] ? false : T extends [infer _Head, ...infer _Rest] ? true : false;
200
-
201
- /**
202
- * Flattens a tuple
203
- */
204
- export type FlattenTuple<A extends unknown[]> = A extends [infer U, ...infer Rest]
205
- ? U extends unknown[]
206
- ? [...U, ...FlattenTuple<Rest>]
207
- : [U, ...FlattenTuple<Rest>]
208
- : [];
209
-
210
- /**
211
- * Flattens an array or tuple
212
- */
213
- export type Flatten<A extends unknown[]> = IsTuple<A> extends true ? FlattenTuple<A> : FlattenArray<A>;
214
-
215
- type _Tuple<T, N extends number, R extends unknown[] = Empty> = R['length'] extends N ? R : _Tuple<T, N, [T, ...R]>;
216
-
217
- /**
218
- * Creates a tuple of T with length N
219
- */
220
- export type Tuple<T, N extends number> = _Tuple<T, N>;
221
-
222
- /**
223
- * Makes all members of the tuple T optional
224
- */
225
- export type OptionalTuple<T extends unknown[]> = T extends [infer Head, ...infer Tail]
226
- ? [Head?, ...OptionalTuple<Tail>]
227
- : T;
228
-
229
- /**
230
- * Keys of a Map
231
- */
232
- export type MapKeys<T> = T extends Map<infer K, any> ? K : never;
233
-
234
- export type ClassLike<Instance = any> = abstract new (...args: any[]) => Instance;
235
-
236
- export type Concrete<T extends ClassLike> = Pick<T, keyof T> & (new (...args: any[]) => InstanceType<T>);
237
-
238
- /**
239
- * Converts a union to an intersection
240
- * @see https://stackoverflow.com/a/55128956/17637456
241
- */
242
- export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
243
-
244
- /**
245
- * Gets the last element of a union
246
- * @see https://stackoverflow.com/a/55128956/17637456
247
- */
248
- export type LastOfUnion<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never;
249
-
250
- /**
251
- * Converts a union to a tuple
252
- * @see https://stackoverflow.com/a/55128956/17637456
253
- */
254
- export type UnionToTuple<T, L = LastOfUnion<T>, N = [T] extends [never] ? true : false> = true extends N
255
- ? []
256
- : Push<UnionToTuple<Exclude<T, L>>, L>;
257
-
258
- /**
259
- * Makes properties with keys assignable to K in T required
260
- * @see https://stackoverflow.com/a/69328045/17637456
261
- */
262
- export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
263
-
264
- /**
265
- * Makes properties with keys assignable to K in T optional
266
- */
267
- export type WithOptional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
268
-
269
- /**
270
- * Nothing in T
271
- */
272
- export type Never<T> = { [K in keyof T]?: never };
273
-
274
- /**
275
- * All of the properties in T or none of them
276
- */
277
- export type AllOrNone<T> = T | Never<T>;
package/src/version.ts DELETED
@@ -1,54 +0,0 @@
1
- import { capitalize } from './string.js';
2
-
3
- export type Sep = '_' | '-';
4
-
5
- export type Part = `${number}.${number}.${number}`;
6
-
7
- export type WithPre = `${Part}${Sep}${string}${Sep | '.'}${Part | number}`;
8
-
9
- export type Full = Part | WithPre;
10
-
11
- type Type<S extends string, Acc extends string = ''> = S extends `${infer First}${infer Rest}`
12
- ? First extends Sep | '.'
13
- ? Acc
14
- : Type<Rest, `${Acc}${First}`>
15
- : Acc;
16
-
17
- export type Parse<T extends Full, StripCore extends boolean> = T extends `${infer Core}${Sep}${infer Rest}`
18
- ? Rest extends `${infer U}`
19
- ? {
20
- full: T;
21
- core: Core;
22
- type: Type<U>;
23
- pre: U extends `${Type<U>}${Sep | '.'}${infer Pre}` ? Pre : '';
24
- display: `${StripCore extends true
25
- ? Core extends '1.0.0'
26
- ? ''
27
- : `${Core} `
28
- : `${Core} `}${Capitalize<Type<U>>}${U extends `${Type<U>}${Sep | '.'}${infer Pre}` ? ` ${Pre}` : ''}`;
29
- }
30
- : never
31
- : T extends Part
32
- ? {
33
- full: T;
34
- core: T;
35
- display: T;
36
- }
37
- : never;
38
-
39
- export const regex = /^(?<core>\d+\.\d+\.\d+)(?:[-_](?<type>[^-_.]+)[-_.](?<pre>\d*(?:\.\d+)*))?/;
40
-
41
- /**
42
- * Parses a semver version, including compile-time results
43
- * @param full the full version to parse
44
- * @param stripCore Whether to strip the leading core version in the display version when the core version is 1.0.0 (default false)
45
- */
46
- export function parse<const T extends Full>(full: T): Parse<T, false>;
47
- export function parse<const T extends Full, const S extends boolean>(full: T, stripCore: S): Parse<T, S>;
48
- export function parse<const T extends Full, const S extends boolean>(full: T, stripCore?: S): Parse<T, S> {
49
- const { type, pre, core } = regex.exec(full)!.groups!;
50
- const display = type
51
- ? `${stripCore && core == '1.0.0' ? '' : core + ' '}${capitalize(type)}${pre ? ` ${pre}` : ''}`
52
- : core;
53
- return { full, core, pre, type, display } as Parse<T, S>;
54
- }
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "lib": ["ESNext"],
4
- "module": "NodeNext",
5
- "target": "ES2022",
6
- "moduleResolution": "NodeNext",
7
- "outDir": "dist",
8
- "typeRoots": ["node_modules/@types"],
9
- "resolveJsonModule": true,
10
- "esModuleInterop": true,
11
- "noImplicitThis": true,
12
- "declaration": true,
13
- "strict": true
14
- },
15
- "typedocOptions": {
16
- "entryPoints": ["src/index.ts"],
17
- "out": "docs"
18
- },
19
- "include": ["./src/**/*.ts"],
20
- "exclude": ["node_modules"]
21
- }