@code-essentials/utils 1.0.2 → 1.0.3

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.
@@ -0,0 +1,19 @@
1
+ export declare class AsyncVariable<T> implements PromiseLike<T> {
2
+ #private;
3
+ get value(): T;
4
+ set value(value: T);
5
+ get error(): any;
6
+ set error(error: any);
7
+ get complete(): boolean;
8
+ constructor();
9
+ init(): Promise<void>;
10
+ read(): Promise<T>;
11
+ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined): PromiseLike<TResult1 | TResult2>;
12
+ set(value: T, throw_if_set?: boolean): Promise<void>;
13
+ reject(error: unknown, throw_if_set?: boolean): Promise<void>;
14
+ static performCallback<R = void>(fn: (cb: (err?: unknown, res?: R) => void) => void): AsyncVariable<R>;
15
+ perform(fn: () => Promise<T>): this;
16
+ timeout(milliseconds: number): this;
17
+ static perform<T>(fn: () => Promise<T>): AsyncVariable<T>;
18
+ static wait(milliseconds: number): AsyncVariable<void>;
19
+ }
@@ -0,0 +1,103 @@
1
+ export class AsyncVariable {
2
+ #result;
3
+ #result_res;
4
+ #result_p;
5
+ #initialization;
6
+ get value() {
7
+ if (!this.#result)
8
+ throw new Error('incomplete');
9
+ if (this.#result.type === "rejected")
10
+ throw this.#result.error;
11
+ return this.#result.value;
12
+ }
13
+ set value(value) {
14
+ this.set(value);
15
+ }
16
+ get error() {
17
+ if (!this.#result)
18
+ throw new Error('incomplete');
19
+ if (this.#result.type === "resolved")
20
+ throw new Error(this.#result.type);
21
+ return this.#result.error;
22
+ }
23
+ set error(error) {
24
+ this.reject(error);
25
+ }
26
+ get complete() {
27
+ return this.#result !== undefined;
28
+ }
29
+ constructor() {
30
+ this.#initialization = new Promise(initialized =>
31
+ // prevents unhandled rejection
32
+ this.#result_p = new Promise(res => {
33
+ this.#result_res = res;
34
+ initialized();
35
+ }));
36
+ }
37
+ async init() {
38
+ await this.#initialization;
39
+ }
40
+ async read() {
41
+ await this.#initialization;
42
+ await this.#result_p;
43
+ return this.value;
44
+ }
45
+ then(onfulfilled, onrejected) {
46
+ return this.read().then(onfulfilled, onrejected);
47
+ }
48
+ async #complete(throw_if_set = true) {
49
+ await this.#initialization;
50
+ if (this.complete) {
51
+ if (throw_if_set)
52
+ throw new Error('already set');
53
+ else
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ async set(value, throw_if_set = true) {
59
+ if (await this.#complete(throw_if_set)) {
60
+ this.#result_res(this.#result = {
61
+ type: "resolved",
62
+ value
63
+ });
64
+ }
65
+ }
66
+ async reject(error, throw_if_set = true) {
67
+ if (await this.#complete(throw_if_set)) {
68
+ this.#result_res(this.#result = {
69
+ type: "rejected",
70
+ error
71
+ });
72
+ }
73
+ }
74
+ static performCallback(fn) {
75
+ const av = new AsyncVariable();
76
+ fn(async (err, res) => {
77
+ if (err)
78
+ await av.error(err);
79
+ else
80
+ await av.set(res);
81
+ });
82
+ return av;
83
+ }
84
+ perform(fn) {
85
+ fn().then(value => this.set(value)).catch(error => this.reject(error));
86
+ return this;
87
+ }
88
+ timeout(milliseconds) {
89
+ AsyncVariable.wait(milliseconds).then(() => {
90
+ if (!this.complete)
91
+ this.reject("timeout");
92
+ });
93
+ return this;
94
+ }
95
+ static perform(fn) {
96
+ return new AsyncVariable().perform(fn);
97
+ }
98
+ static wait(milliseconds) {
99
+ const res = new AsyncVariable();
100
+ setTimeout(() => res.set(), milliseconds);
101
+ return res;
102
+ }
103
+ }
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export * from './async-variable.js';
3
+ export * from './observable-list.js';
4
+ export * from './object.js';
5
+ export * from './lock.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export * from './async-variable.js';
3
+ export * from './observable-list.js';
4
+ export * from './object.js';
5
+ export * from './lock.js';
package/dist/lock.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export declare class Lock<Of = void> {
2
+ #private;
3
+ get isAcquired(): boolean;
4
+ acquire(of: Of): LockContext<Of>;
5
+ protected release(_context: LockContext<Of>): void;
6
+ static readonly ERR_ACQUIRED = "lock currently acquired";
7
+ }
8
+ export declare class LockContext<Of> implements Disposable {
9
+ #private;
10
+ readonly lock: Lock<Of>;
11
+ readonly of: Of;
12
+ constructor(lock: Lock<Of>, of: Of, release: (conext: LockContext<Of>) => void);
13
+ release(): void;
14
+ [Symbol.dispose](): void;
15
+ static readonly ERR_LOCK_RELEASED = "lock context already released";
16
+ }
package/dist/lock.js ADDED
@@ -0,0 +1,39 @@
1
+ export class Lock {
2
+ #isAcquired = false;
3
+ get isAcquired() {
4
+ return this.#isAcquired;
5
+ }
6
+ acquire(of) {
7
+ if (this.#isAcquired)
8
+ throw new Error(Lock.ERR_ACQUIRED);
9
+ this.#isAcquired = true;
10
+ return new LockContext(this, of, this.release.bind(this));
11
+ }
12
+ release(_context) {
13
+ if (!this.isAcquired)
14
+ throw new Error();
15
+ this.#isAcquired = false;
16
+ }
17
+ static ERR_ACQUIRED = 'lock currently acquired';
18
+ }
19
+ export class LockContext {
20
+ lock;
21
+ of;
22
+ #release;
23
+ #isAcquired = true;
24
+ constructor(lock, of, release) {
25
+ this.lock = lock;
26
+ this.of = of;
27
+ this.#release = release;
28
+ }
29
+ release() {
30
+ if (!this.#isAcquired)
31
+ throw new Error(LockContext.ERR_LOCK_RELEASED);
32
+ this.#isAcquired = false;
33
+ this.#release(this);
34
+ }
35
+ [Symbol.dispose]() {
36
+ this.release();
37
+ }
38
+ static ERR_LOCK_RELEASED = "lock context already released";
39
+ }
@@ -0,0 +1,10 @@
1
+ export type Values<T extends object> = T[keyof T];
2
+ export type PropertyPath<T> = [] | T extends object ? Values<{
3
+ [K in keyof T]: [K] | [K, Values<PropertyPath<T[K]>>];
4
+ }> : [];
5
+ export type PropertyType<T, Property extends PropertyPath<T>> = PropertyType_<T, Property>;
6
+ type PropertyType_<T, Property extends any[], Constructed extends any[] = []> = (Property & Constructed) extends never ? Values<{
7
+ [K in keyof T as Property extends [...Constructed, K, ...any[]] ? K : never]: PropertyType_<T[K], Property, [...Constructed, K]>;
8
+ }> : T;
9
+ export declare function replaceProperty<T, Property extends PropertyPath<T> = PropertyPath<T>>(obj: T, property: Property, value: any): T;
10
+ export {};
package/dist/object.js ADDED
@@ -0,0 +1,34 @@
1
+ // export type PropertyType<T, Property extends PropertyPath<T> & PropertyKey[]> = PropertyType_<T, Property>
2
+ // type PropertyType_<T, Property extends PropertyKey[], Constructed extends PropertyKey[] = []> =
3
+ // (Property & Constructed) extends never ?
4
+ // Values<{
5
+ // [K in keyof T as Property extends [...Constructed, K, ...PropertyKey[]] ? K : never]:
6
+ // PropertyType_<T[K], Property, [...Constructed, K]>
7
+ // }> :
8
+ // T
9
+ // type A = {
10
+ // a: {
11
+ // a: 10,
12
+ // b: 1
13
+ // c: 2
14
+ // }
15
+ // b: {
16
+ // a: {
17
+ // c: 1
18
+ // }
19
+ // }
20
+ // }
21
+ // type a_path = PropertyPath<A>
22
+ // const ac = ["a", "c"] satisfies a_path
23
+ // // type a1 = (typeof ac) extends ["a", "c", ...PropertyKey[]] ? true : false
24
+ // type ac_type = PropertyType_<A, typeof ac>
25
+ export function replaceProperty(obj, property, value) {
26
+ if (property.length === 0)
27
+ return (value ?? obj);
28
+ if (typeof obj !== 'object')
29
+ throw new Error();
30
+ const prototype = Object.create(obj);
31
+ const property0 = property[0];
32
+ prototype[property0] = replaceProperty(obj[property0], property.slice(1), value);
33
+ return prototype;
34
+ }
@@ -0,0 +1,24 @@
1
+ export interface ObservableListProtocols<T> {
2
+ insert(item: T, index: number): void;
3
+ delete(item: T, index: number): void;
4
+ reorder(item: T, index1: number): void;
5
+ }
6
+ export declare class ObservableList<T> extends Array<T> {
7
+ #private;
8
+ constructor(length: number);
9
+ constructor(...items: T[]);
10
+ push(...items: T[]): number;
11
+ pop(): T | undefined;
12
+ reverse(): T[];
13
+ sort(compareFn?: ((a: T, b: T) => number) | undefined): this;
14
+ shift(): T | undefined;
15
+ splice(start: number, deleteCount?: number, ...items: T[]): T[];
16
+ unshift(...items: T[]): number;
17
+ on<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol, responder: ObservableListProtocols<T>[Protocol]): void;
18
+ off<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol, responder: ObservableListProtocols<T>[Protocol]): void;
19
+ responders<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol): {
20
+ insert: Set<(item: T, index: number) => void>;
21
+ delete: Set<(item: T, index: number) => void>;
22
+ reorder: Set<(item: T, index1: number) => void>;
23
+ }[Protocol];
24
+ }
@@ -0,0 +1,75 @@
1
+ export class ObservableList extends Array {
2
+ constructor(...itemsOrArrayLength) {
3
+ super(...itemsOrArrayLength);
4
+ }
5
+ push(...items) {
6
+ const length0 = this.length;
7
+ const result = super.push(...items);
8
+ items.forEach((item, i) => this.#emit("insert", item, length0 + i));
9
+ return result;
10
+ }
11
+ pop() {
12
+ const index = this.length - 1;
13
+ const result = super.pop();
14
+ if (index >= 0)
15
+ this.#emit("delete", result, index);
16
+ return result;
17
+ }
18
+ reverse() {
19
+ const final_index = this.length - 1;
20
+ const result = super.reverse();
21
+ this.forEach((item, i) => {
22
+ const index0 = final_index - i;
23
+ const index1 = i;
24
+ if (index0 !== index1)
25
+ this.#emit("reorder", item, index1);
26
+ });
27
+ return result;
28
+ }
29
+ sort(compareFn) {
30
+ const result = super.sort(compareFn);
31
+ this.forEach((item, i) => this.#emit("reorder", item, i));
32
+ return result;
33
+ }
34
+ shift() {
35
+ const length0 = this.length;
36
+ const result = super.shift();
37
+ if (length0 > 0)
38
+ this.#emit("delete", result, 0);
39
+ this.forEach((item, i) => this.#emit("reorder", item, i));
40
+ return result;
41
+ }
42
+ splice(start, deleteCount = 0, ...items) {
43
+ const deleted = super.splice(start, deleteCount, ...items);
44
+ deleted.forEach((item, i) => this.#emit("delete", item, start + i));
45
+ if ((deleteCount ?? 0) !== (items?.length ?? 0))
46
+ for (let i = start + (items?.length ?? 0); i < this.length; i++)
47
+ this.#emit("reorder", this[i], i);
48
+ items.forEach((item, i) => this.#emit("insert", item, start + i));
49
+ return deleted;
50
+ }
51
+ unshift(...items) {
52
+ const result = super.unshift(...items);
53
+ for (let i = items.length; i < this.length; i++)
54
+ this.#emit("reorder", this[i], i);
55
+ items.forEach((item, i) => this.#emit("insert", item, i));
56
+ return result;
57
+ }
58
+ #responders = {
59
+ insert: new Set(),
60
+ delete: new Set(),
61
+ reorder: new Set(),
62
+ };
63
+ #emit(protocol, ...parameters) {
64
+ return [...this.#responders[protocol]].map(responder => responder(...parameters));
65
+ }
66
+ on(protocol, responder) {
67
+ this.#responders[protocol].add(responder);
68
+ }
69
+ off(protocol, responder) {
70
+ this.#responders[protocol].delete(responder);
71
+ }
72
+ responders(protocol) {
73
+ return this.#responders[protocol];
74
+ }
75
+ }
@@ -0,0 +1,12 @@
1
+ export type Deleteable<T, K extends keyof T = keyof T> = {
2
+ -readonly [K1 in K]?: T[K1];
3
+ };
4
+ export type PartlyDeleteable<T, K extends keyof T> = {
5
+ [K1 in keyof T as (K1 extends K ? never : K1)]: T[K1];
6
+ } & Deleteable<T, K>;
7
+ export type UndefinedIf<T, Condition extends boolean = boolean> = Condition extends true ? undefined : T;
8
+ export declare function undefinedIf<T, Condition extends boolean = boolean>(condition: Condition, item: () => T): UndefinedIf<T, Condition>;
9
+ export type Prefixed<Prefix extends string, T extends object> = {
10
+ [K in string & keyof T as `${Prefix}${K}`]: T[K];
11
+ };
12
+ export declare function prefixed<Prefix extends string, T extends object>(prefix: Prefix, o: T): Prefixed<Prefix, T>;
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ export function undefinedIf(condition, item) {
2
+ return (condition ? undefined : item());
3
+ }
4
+ export function prefixed(prefix, o) {
5
+ return Object.fromEntries(Object.entries(o).map(([k, v]) => [`${prefix}${k}`, v]));
6
+ }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@code-essentials/utils",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist/**/*"
10
+ ],
8
11
  "scripts": {
9
12
  "clean": "rm -rf dist",
10
13
  "prebuild": "npm run clean",
@@ -17,7 +20,7 @@
17
20
  },
18
21
  "devDependencies": {
19
22
  "@ava/typescript": "^6.0.0",
20
- "@code-essentials/tsconfig": "^1.0",
23
+ "@code-essentials/tsconfig": "^1.0.3",
21
24
  "@types/node": "^24.5.2",
22
25
  "ava": "^6.4.1",
23
26
  "typescript": "^5.9.2"
@@ -1,21 +0,0 @@
1
- {
2
- "configurations": [
3
- {
4
- "name": "test",
5
- "request": "launch",
6
- "runtimeArgs": [
7
- "run-script",
8
- "test"
9
- ],
10
- "runtimeExecutable": "pnpm",
11
- "sourceMaps": true,
12
- "pauseForSourceMap": true,
13
- "preLaunchTask": "npm: build:debug",
14
- "internalConsoleOptions": "neverOpen",
15
- "skipFiles": [
16
- "<node_internals>/**"
17
- ],
18
- "type": "node"
19
- }
20
- ]
21
- }
@@ -1,22 +0,0 @@
1
- {
2
- "version": "2.0.0",
3
- "tasks": [
4
- {
5
- "label": "npm: build:debug",
6
- "type": "shell",
7
- "command": "npm",
8
- "args": [
9
- "run-script",
10
- "build:debug",
11
- ],
12
- "problemMatcher": [
13
- "$tsc"
14
- ],
15
- "presentation": {
16
- "reveal": "silent",
17
- "close": true,
18
- },
19
- "group": "build"
20
- }
21
- ]
22
- }
@@ -1,146 +0,0 @@
1
- interface AsyncVariableResultBase<Type> {
2
- readonly type: Type
3
- }
4
-
5
- interface AsyncVariableResultResolved<T> extends AsyncVariableResultBase<"resolved"> {
6
- readonly value: T
7
- }
8
-
9
- interface AsyncVariableResultRejected extends AsyncVariableResultBase<"rejected"> {
10
- readonly error: any
11
- }
12
-
13
- type AsyncVariableResult<T> =
14
- | AsyncVariableResultResolved<T>
15
- | AsyncVariableResultRejected
16
-
17
- export class AsyncVariable<T> implements PromiseLike<T> {
18
- #result: AsyncVariableResult<T> | undefined
19
- #result_res!: (res: AsyncVariableResult<T>) => void
20
- #result_p!: Promise<AsyncVariableResult<T>>
21
- readonly #initialization: Promise<void>
22
-
23
- get value(): T {
24
- if (!this.#result)
25
- throw new Error('incomplete')
26
-
27
- if (this.#result.type === "rejected")
28
- throw this.#result.error
29
-
30
- return this.#result.value
31
- }
32
-
33
- set value(value) {
34
- this.set(value)
35
- }
36
-
37
- get error() {
38
- if (!this.#result)
39
- throw new Error('incomplete')
40
-
41
- if (this.#result.type === "resolved")
42
- throw new Error(this.#result.type)
43
-
44
- return this.#result.error
45
- }
46
-
47
- set error(error) {
48
- this.reject(error)
49
- }
50
-
51
- get complete() {
52
- return this.#result !== undefined
53
- }
54
-
55
- constructor() {
56
- this.#initialization = new Promise(initialized =>
57
- // prevents unhandled rejection
58
- this.#result_p = new Promise(res => {
59
- this.#result_res = res
60
- initialized()
61
- })
62
- )
63
- }
64
-
65
- async init() {
66
- await this.#initialization
67
- }
68
-
69
- async read() {
70
- await this.#initialization
71
- await this.#result_p
72
- return this.value
73
- }
74
-
75
- then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined): PromiseLike<TResult1 | TResult2> {
76
- return this.read().then(onfulfilled, onrejected)
77
- }
78
-
79
- async #complete(throw_if_set = true) {
80
- await this.#initialization
81
- if (this.complete) {
82
- if (throw_if_set)
83
- throw new Error('already set')
84
- else
85
- return false
86
- }
87
-
88
- return true
89
- }
90
-
91
- async set(value: T, throw_if_set = true) {
92
- if (await this.#complete(throw_if_set)) {
93
- this.#result_res(this.#result = {
94
- type: "resolved",
95
- value
96
- })
97
- }
98
- }
99
-
100
- async reject(error: unknown, throw_if_set = true) {
101
- if (await this.#complete(throw_if_set)) {
102
- this.#result_res(this.#result = {
103
- type: "rejected",
104
- error
105
- })
106
- }
107
- }
108
-
109
- static performCallback<R = void>(fn: (cb: (err?: unknown, res?: R) => void) => void): AsyncVariable<R> {
110
- const av = new AsyncVariable<R>()
111
-
112
- fn(async (err, res) => {
113
- if (err)
114
- await av.error(err)
115
- else
116
- await av.set(<R>res)
117
- })
118
-
119
- return av
120
- }
121
-
122
- perform(fn: () => Promise<T>): this {
123
- fn().then(value => this.set(value)).catch(error => this.reject(error))
124
-
125
- return this
126
- }
127
-
128
- timeout(milliseconds: number): this {
129
- AsyncVariable.wait(milliseconds).then(() => {
130
- if (!this.complete)
131
- this.reject("timeout")
132
- })
133
-
134
- return this
135
- }
136
-
137
- static perform<T>(fn: () => Promise<T>): AsyncVariable<T> {
138
- return new AsyncVariable<T>().perform(fn)
139
- }
140
-
141
- static wait(milliseconds: number) {
142
- const res = new AsyncVariable<void>()
143
- setTimeout(() => res.set(), milliseconds)
144
- return res
145
- }
146
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './types.js'
2
- export * from './async-variable.js'
3
- export * from './observable-list.js'
4
- export * from './object.js'
5
- export * from './lock.js'
package/src/lock.spec.ts DELETED
@@ -1,38 +0,0 @@
1
- import test from "ava"
2
- import { Lock } from "./lock.js"
3
-
4
- test('acquire when free', t => {
5
- const lock = new Lock()
6
- const context = lock.acquire()
7
- context.release()
8
- t.pass()
9
- })
10
-
11
- test('fail to acquire when already acquired', t => {
12
- const lock = new Lock()
13
- lock.acquire()
14
- t.throws(() => lock.acquire(), { message: Lock.ERR_ACQUIRED })
15
- })
16
-
17
- test('disposable release', t => {
18
- const lock = new Lock()
19
-
20
- function acquire(pass: boolean) {
21
- if (pass)
22
- t.notThrows(() => lock.acquire())
23
- else
24
- t.throws(() => lock.acquire(), { message: Lock.ERR_ACQUIRED })
25
- }
26
-
27
- function acquire_0() {
28
- using _context1 = lock.acquire()
29
- acquire(false)
30
- }
31
-
32
- function acquire_1() {
33
- acquire(true)
34
- }
35
-
36
- acquire_0()
37
- acquire_1()
38
- })
package/src/lock.ts DELETED
@@ -1,50 +0,0 @@
1
- export class Lock<Of = void> {
2
- #isAcquired = false
3
-
4
- get isAcquired() {
5
- return this.#isAcquired
6
- }
7
-
8
- acquire(of: Of): LockContext<Of> {
9
- if (this.#isAcquired)
10
- throw new Error(Lock.ERR_ACQUIRED)
11
- this.#isAcquired = true
12
- return new LockContext(this, of, this.release.bind(this))
13
- }
14
-
15
- protected release(_context: LockContext<Of>) {
16
- if (!this.isAcquired)
17
- throw new Error()
18
-
19
- this.#isAcquired = false
20
- }
21
-
22
- static readonly ERR_ACQUIRED = 'lock currently acquired'
23
- }
24
-
25
- export class LockContext<Of> implements Disposable {
26
- readonly #release: (conext: LockContext<Of>) => void
27
- #isAcquired = true
28
-
29
- constructor(
30
- readonly lock: Lock<Of>,
31
- readonly of: Of,
32
- release: (conext: LockContext<Of>) => void
33
- ) {
34
- this.#release = release
35
- }
36
-
37
- release() {
38
- if (!this.#isAcquired)
39
- throw new Error(LockContext.ERR_LOCK_RELEASED)
40
-
41
- this.#isAcquired = false
42
- this.#release(this)
43
- }
44
-
45
- [Symbol.dispose]() {
46
- this.release()
47
- }
48
-
49
- static readonly ERR_LOCK_RELEASED = "lock context already released"
50
- }
@@ -1,39 +0,0 @@
1
- import test from "ava"
2
- import { replaceProperty } from "./object.js"
3
-
4
- test("replace", t => {
5
- interface A {
6
- i: number
7
- b: B
8
- }
9
-
10
- interface B {
11
- j: number
12
- c: C
13
- }
14
-
15
- interface C {
16
- k: number
17
- }
18
-
19
- const a0: A = {
20
- i: 0,
21
- b: {
22
- j: 0,
23
- c: {
24
- k: 0
25
- },
26
- }
27
- }
28
-
29
- t.is(a0.i, 0)
30
-
31
- const a1 = replaceProperty(a0, ["i"], 1)
32
-
33
- t.is(a0.i, 0)
34
- t.is(a1.i, 1)
35
-
36
- a0.b.j++
37
- t.is(a0.b.j, 1)
38
- t.is(a1.b.j, 1)
39
- })
package/src/object.ts DELETED
@@ -1,60 +0,0 @@
1
- export type Values<T extends object> = T[keyof T]
2
-
3
- export type PropertyPath<T> =
4
- | []
5
- | T extends object ? Values<{
6
- [K in keyof T]: [K] | [K, Values<PropertyPath<T[K]>>]
7
- }> : []
8
-
9
- export type PropertyType<T, Property extends PropertyPath<T>> = PropertyType_<T, Property>
10
- type PropertyType_<T, Property extends any[], Constructed extends any[] = []> =
11
- (Property & Constructed) extends never ?
12
- Values<{
13
- [K in keyof T as Property extends [...Constructed, K, ...any[]] ? K : never]:
14
- PropertyType_<T[K], Property, [...Constructed, K]>
15
- }> :
16
- T
17
-
18
- // export type PropertyType<T, Property extends PropertyPath<T> & PropertyKey[]> = PropertyType_<T, Property>
19
- // type PropertyType_<T, Property extends PropertyKey[], Constructed extends PropertyKey[] = []> =
20
- // (Property & Constructed) extends never ?
21
- // Values<{
22
- // [K in keyof T as Property extends [...Constructed, K, ...PropertyKey[]] ? K : never]:
23
- // PropertyType_<T[K], Property, [...Constructed, K]>
24
- // }> :
25
- // T
26
-
27
- // type A = {
28
- // a: {
29
- // a: 10,
30
- // b: 1
31
- // c: 2
32
- // }
33
- // b: {
34
- // a: {
35
- // c: 1
36
- // }
37
- // }
38
- // }
39
-
40
- // type a_path = PropertyPath<A>
41
- // const ac = ["a", "c"] satisfies a_path
42
- // // type a1 = (typeof ac) extends ["a", "c", ...PropertyKey[]] ? true : false
43
- // type ac_type = PropertyType_<A, typeof ac>
44
-
45
- export function replaceProperty<T, Property extends PropertyPath<T> = PropertyPath<T>>(
46
- obj: T,
47
- property: Property,
48
- value: any
49
- ): T {
50
- if (property.length === 0)
51
- return <T>((<any>value) ?? obj)
52
-
53
- if (typeof obj !== 'object')
54
- throw new Error()
55
-
56
- const prototype = Object.create(obj)
57
- const property0 = property[0]!
58
- prototype[property0] = replaceProperty<any>((<any>obj)[property0]!, <any>property.slice(1), value)
59
- return <T>prototype
60
- }
@@ -1,25 +0,0 @@
1
- import { ObservableList, ObservableListProtocols } from "./observable-list.js"
2
- import test from "ava"
3
-
4
- test("insert", t => {
5
- const list = new ObservableList<string>()
6
-
7
- const words = ["keyboard", "mouse", "display"]
8
- t.plan(words.length)
9
-
10
- let itemInserted = ""
11
- const responder: ObservableListProtocols<string>["insert"] = (item) => t.is(item, itemInserted)
12
-
13
- list.on("insert", responder)
14
-
15
- list.push(itemInserted = words[0]!)
16
- list.push(itemInserted = words[1]!)
17
-
18
- list.off("insert", responder)
19
-
20
- list.push("unrelated")
21
-
22
- list.on("insert", responder)
23
-
24
- list.push(itemInserted = words[2]!)
25
- })
@@ -1,114 +0,0 @@
1
- export interface ObservableListProtocols<T> {
2
- insert(item: T, index: number): void
3
- delete(item: T, index: number): void
4
- reorder(item: T, index1: number): void
5
- }
6
-
7
- export class ObservableList<T> extends Array<T> {
8
- constructor(length: number)
9
- constructor(...items: T[])
10
- constructor(...itemsOrArrayLength: any[]) {
11
- super(...itemsOrArrayLength)
12
- }
13
-
14
- override push(...items: T[]): number {
15
- const length0 = this.length
16
- const result = super.push(...items)
17
-
18
- items.forEach((item, i) => this.#emit("insert", item, length0 + i))
19
-
20
- return result
21
- }
22
-
23
- override pop(): T | undefined {
24
- const index = this.length - 1
25
- const result = super.pop()
26
-
27
- if (index >= 0)
28
- this.#emit("delete", result!, index)
29
-
30
- return result
31
- }
32
-
33
- override reverse(): T[] {
34
- const final_index = this.length - 1
35
- const result = super.reverse()
36
-
37
- this.forEach((item, i) => {
38
- const index0 = final_index - i
39
- const index1 = i
40
-
41
- if (index0 !== index1)
42
- this.#emit("reorder", item, index1)
43
- })
44
-
45
- return result
46
- }
47
-
48
- override sort(compareFn?: ((a: T, b: T) => number) | undefined): this {
49
- const result = super.sort(compareFn)
50
-
51
- this.forEach((item, i) => this.#emit("reorder", item, i))
52
-
53
- return result
54
- }
55
-
56
- override shift(): T | undefined {
57
- const length0 = this.length
58
- const result = super.shift()
59
-
60
- if (length0 > 0)
61
- this.#emit("delete", result!, 0)
62
-
63
- this.forEach((item, i) => this.#emit("reorder", item, i))
64
-
65
- return result
66
- }
67
-
68
- override splice(start: number, deleteCount = 0, ...items: T[]): T[] {
69
- const deleted = super.splice(start, deleteCount, ...items)
70
-
71
- deleted.forEach((item, i) => this.#emit("delete", item, start + i))
72
-
73
- if ((deleteCount ?? 0) !== (items?.length ?? 0))
74
- for (let i = start + (items?.length ?? 0); i < this.length; i++)
75
- this.#emit("reorder", this[i]!, i)
76
-
77
- items.forEach((item, i) => this.#emit("insert", item, start + i))
78
-
79
- return deleted
80
- }
81
-
82
- override unshift(...items: T[]): number {
83
- const result = super.unshift(...items)
84
-
85
- for (let i = items.length; i < this.length; i++)
86
- this.#emit("reorder", this[i]!, i)
87
-
88
- items.forEach((item, i) => this.#emit("insert", item, i))
89
-
90
- return result
91
- }
92
-
93
- #responders: { [Protocol in keyof ObservableListProtocols<T>]: Set<ObservableListProtocols<T>[Protocol]> } = {
94
- insert: new Set(),
95
- delete: new Set(),
96
- reorder: new Set(),
97
- }
98
-
99
- #emit<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol, ...parameters: Parameters<ObservableListProtocols<T>[Protocol]>): ReturnType<ObservableListProtocols<T>[Protocol]>[] {
100
- return [...this.#responders[protocol]].map(responder => (<any>responder)(...parameters))
101
- }
102
-
103
- on<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol, responder: ObservableListProtocols<T>[Protocol]) {
104
- this.#responders[protocol].add(responder)
105
- }
106
-
107
- off<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol, responder: ObservableListProtocols<T>[Protocol]) {
108
- this.#responders[protocol].delete(responder)
109
- }
110
-
111
- responders<Protocol extends keyof ObservableListProtocols<T>>(protocol: Protocol) {
112
- return this.#responders[protocol]
113
- }
114
- }
package/src/types.ts DELETED
@@ -1,21 +0,0 @@
1
- export type Deleteable<T, K extends keyof T = keyof T> = {
2
- -readonly [K1 in K]?: T[K1]
3
- }
4
-
5
- export type PartlyDeleteable<T, K extends keyof T> = {
6
- [K1 in keyof T as (K1 extends K ? never : K1)]: T[K1]
7
- } & Deleteable<T, K>
8
-
9
- export type UndefinedIf<T, Condition extends boolean = boolean> = Condition extends true ? undefined : T
10
-
11
- export function undefinedIf<T, Condition extends boolean = boolean>(condition: Condition, item: () => T): UndefinedIf<T, Condition> {
12
- return <UndefinedIf<T, Condition>>(condition ? undefined : item())
13
- }
14
-
15
- export type Prefixed<Prefix extends string, T extends object> = {
16
- [K in string & keyof T as `${Prefix}${K}`]: T[K]
17
- }
18
-
19
- export function prefixed<Prefix extends string, T extends object>(prefix: Prefix, o: T): Prefixed<Prefix, T> {
20
- return <Prefixed<Prefix, T>>Object.fromEntries(Object.entries(o).map(([k, v]) => [`${prefix}${k}`, v] as const))
21
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "@code-essentials/tsconfig/tsconfig.debug.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src",
6
- },
7
- "include": ["src", "src/**/*.spec.ts"],
8
- "exclude": ["node_modules"],
9
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "@code-essentials/tsconfig/tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src",
6
- },
7
- "include": ["src"],
8
- "exclude": ["node_modules", "src/**/*.spec.ts"],
9
- }