@planet-matrix/mobius-model 0.9.0 → 0.10.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.
- package/CHANGELOG.md +13 -0
- package/oxlint.config.ts +1 -2
- package/package.json +5 -5
- package/scripts/build.ts +2 -52
- package/src/basic/promise.ts +141 -71
- package/src/drizzle/pagination.ts +0 -2
- package/src/event/class-event-proxy.ts +0 -2
- package/src/event/instance-event-proxy.ts +0 -2
- package/src/exception/README.md +28 -19
- package/src/exception/error/error.ts +123 -0
- package/src/exception/error/index.ts +2 -0
- package/src/exception/error/match.ts +38 -0
- package/src/exception/error/must-fix.ts +17 -0
- package/src/exception/index.ts +2 -0
- package/src/file-system/find.ts +53 -0
- package/src/file-system/index.ts +2 -0
- package/src/file-system/path.ts +76 -0
- package/src/file-system/resolve.ts +22 -0
- package/src/form/inputor-controller/base.ts +0 -13
- package/src/form/inputor-controller/form.ts +0 -2
- package/src/http/api/api-type.ts +0 -3
- package/src/http/api-adapter/api-result-arktype.ts +0 -3
- package/src/index.ts +2 -0
- package/src/openai/openai.ts +0 -1
- package/src/request/fetch/browser.ts +0 -5
- package/src/request/fetch/nodejs.ts +0 -5
- package/src/request/request/base.ts +0 -4
- package/src/request/request/general.ts +0 -1
- package/src/result/controller.ts +11 -7
- package/src/result/either.ts +230 -60
- package/src/result/generator.ts +168 -0
- package/src/result/index.ts +1 -0
- package/src/route/router/router.ts +0 -1
- package/src/route/uri/hash.ts +0 -1
- package/src/route/uri/search.ts +0 -1
- package/src/service/README.md +1 -0
- package/src/service/index.ts +1 -0
- package/src/service/service.ts +110 -0
- package/src/socket/client/socket-unit.ts +0 -2
- package/src/socket/server/socket-unit.ts +0 -1
- package/src/tube/helper.ts +0 -1
- package/src/weixin/official-account/authorization.ts +0 -2
- package/src/weixin/official-account/js-api.ts +0 -2
- package/src/weixin/open/oauth2.ts +0 -2
- package/tests/unit/aio/json.spec.ts +0 -1
- package/tests/unit/basic/promise.spec.ts +158 -50
- package/tests/unit/credential/api-key.spec.ts +0 -1
- package/tests/unit/credential/password.spec.ts +0 -1
- package/tests/unit/exception/error/error.spec.ts +83 -0
- package/tests/unit/exception/error/match.spec.ts +81 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +0 -4
- package/tests/unit/identifier/uuid.spec.ts +0 -1
- package/tests/unit/request/request/base.spec.ts +0 -3
- package/tests/unit/request/request/general.spec.ts +0 -1
- package/tests/unit/result/controller.spec.ts +82 -0
- package/tests/unit/result/either.spec.ts +377 -0
- package/tests/unit/result/generator.spec.ts +273 -0
- package/tests/unit/route/router/route.spec.ts +0 -1
- package/tests/unit/route/uri/pathname.spec.ts +0 -1
- package/tests/unit/socket/server.spec.ts +0 -2
- package/vite.config.ts +2 -1
- package/dist/index.js +0 -720
- package/dist/index.js.map +0 -1005
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @planet-matrix/mobius-model
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d7c33d7: Add `dist` to `.npmignore`.
|
|
8
|
+
|
|
9
|
+
## 0.10.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 6f99535: Add service model.
|
|
14
|
+
- 9520154: Add tagged error capabilities to exception model.
|
|
15
|
+
|
|
3
16
|
## 0.9.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/oxlint.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planet-matrix/mobius-model",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Mobius model.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mobius",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"prepublishOnly": "bun run build"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
+
"typescript": "^6.0.2",
|
|
62
63
|
"ua-parser-js": "^2.0.9",
|
|
63
64
|
"html-to-image": "^1.11.13",
|
|
64
65
|
"undici": "^7.24.6",
|
|
@@ -77,14 +78,13 @@
|
|
|
77
78
|
"openai": "^6.33.0"
|
|
78
79
|
},
|
|
79
80
|
"devDependencies": {
|
|
80
|
-
"@planet-matrix/mobius-mono": "0.
|
|
81
|
+
"@planet-matrix/mobius-mono": "0.10.1",
|
|
81
82
|
"@types/bun": "^1.3.11",
|
|
83
|
+
"@typescript/native-preview": "^7.0.0-dev.20260329.1",
|
|
82
84
|
"oxlint": "^1.57.0",
|
|
83
85
|
"oxlint-tsgolint": "^0.18.1",
|
|
84
|
-
"typescript": "^6.0.2",
|
|
85
|
-
"@typescript/native-preview": "^7.0.0-dev.20260329.1",
|
|
86
|
-
"vite": "^8.0.3",
|
|
87
86
|
"vitest": "^4.1.2",
|
|
87
|
+
"vite": "^8.0.3",
|
|
88
88
|
"drizzle-kit": "^0.31.10"
|
|
89
89
|
},
|
|
90
90
|
"peerDependencies": {},
|
package/scripts/build.ts
CHANGED
|
@@ -1,53 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Build } from "@planet-matrix/mobius-mono"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
console.log("Cleaning and preparing ./dist directory...")
|
|
5
|
-
await $`rm -rf dist`
|
|
6
|
-
|
|
7
|
-
// console.log("Building TypeScript declarations...")
|
|
8
|
-
// await $`bun run tsgo --noEmit false`
|
|
9
|
-
|
|
10
|
-
console.log("Building the library with Bun...")
|
|
11
|
-
const builtOutput = await build({
|
|
12
|
-
banner: "// Enjoy using Example Library!",
|
|
13
|
-
bytecode: false,
|
|
14
|
-
conditions: [],
|
|
15
|
-
define: {},
|
|
16
|
-
drop: [],
|
|
17
|
-
emitDCEAnnotations: false,
|
|
18
|
-
entrypoints: ["./src/index.ts"],
|
|
19
|
-
env: "PUBLIC_*",
|
|
20
|
-
external: [],
|
|
21
|
-
footer: "// Made with ♥ by the Planet Matrix team!",
|
|
22
|
-
format: "esm",
|
|
23
|
-
ignoreDCEAnnotations: false,
|
|
24
|
-
loader: {},
|
|
25
|
-
minify: {
|
|
26
|
-
identifiers: true,
|
|
27
|
-
keepNames: false,
|
|
28
|
-
syntax: true,
|
|
29
|
-
whitespace: true,
|
|
30
|
-
},
|
|
31
|
-
naming: {
|
|
32
|
-
entry: '[dir]/[name].[ext]',
|
|
33
|
-
chunk: '[name]-[hash].[ext]',
|
|
34
|
-
asset: '[name]-[hash].[ext]',
|
|
35
|
-
},
|
|
36
|
-
outdir: "./dist",
|
|
37
|
-
packages: "bundle",
|
|
38
|
-
plugins: [],
|
|
39
|
-
// publicPath: undefined,
|
|
40
|
-
// root: undefined,
|
|
41
|
-
sourcemap: "linked",
|
|
42
|
-
splitting: true,
|
|
43
|
-
target: "bun",
|
|
44
|
-
throw: false,
|
|
45
|
-
tsconfig: "./tsconfig.json",
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
if (builtOutput.success === false) {
|
|
49
|
-
console.error('Build failed:', builtOutput.logs);
|
|
50
|
-
throw new Error('Build process failed.');
|
|
51
|
-
} else {
|
|
52
|
-
console.log("Build completed successfully.");
|
|
53
|
-
}
|
|
3
|
+
await Build.Library.build({})
|
package/src/basic/promise.ts
CHANGED
|
@@ -6,16 +6,16 @@ import { isPlainObject } from "./is.ts"
|
|
|
6
6
|
* @example
|
|
7
7
|
* ```
|
|
8
8
|
* // Expect: 6
|
|
9
|
-
* const example1 = await promiseThen((value: number) => value * 2
|
|
9
|
+
* const example1 = await promiseThen(Promise.resolve(3), (value: number) => value * 2)
|
|
10
10
|
* // Expect: "ok!"
|
|
11
|
-
* const example2 = await promiseThen((value: string) => `${value}
|
|
11
|
+
* const example2 = await promiseThen(Promise.resolve("ok"), (value: string) => `${value}!`)
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
export const promiseThen = async <V, R>(
|
|
15
|
-
doSomething: (value: V) => R | PromiseLike<R>,
|
|
16
15
|
target: Promise<V>,
|
|
16
|
+
doSomething: (value: V) => R | PromiseLike<R>,
|
|
17
17
|
): Promise<R> => {
|
|
18
|
-
return await target.then(doSomething)
|
|
18
|
+
return await target.then((value) => doSomething(value))
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -24,16 +24,16 @@ export const promiseThen = async <V, R>(
|
|
|
24
24
|
* @example
|
|
25
25
|
* ```
|
|
26
26
|
* // Expect: "fallback"
|
|
27
|
-
* const example1 = await promiseCatch(
|
|
27
|
+
* const example1 = await promiseCatch(Promise.reject(new Error("x")), () => "fallback")
|
|
28
28
|
* // Expect: 3
|
|
29
|
-
* const example2 = await promiseCatch(() => 0
|
|
29
|
+
* const example2 = await promiseCatch(Promise.resolve(3), () => 0)
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
export const promiseCatch = async <V, R>(
|
|
33
|
-
doSomething: (reason: unknown) => R | PromiseLike<R>,
|
|
34
33
|
target: Promise<V>,
|
|
34
|
+
doSomething: (reason: unknown) => R | PromiseLike<R>,
|
|
35
35
|
): Promise<V | R> => {
|
|
36
|
-
return await target.catch(doSomething)
|
|
36
|
+
return await target.catch((reason: unknown) => doSomething(reason))
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -43,25 +43,54 @@ export const promiseCatch = async <V, R>(
|
|
|
43
43
|
* ```
|
|
44
44
|
* let cleaned = false
|
|
45
45
|
* // Expect: 10
|
|
46
|
-
* const example1 = await promiseFinally(() => { cleaned = true }
|
|
46
|
+
* const example1 = await promiseFinally(Promise.resolve(10), () => { cleaned = true })
|
|
47
47
|
* // Expect: true
|
|
48
48
|
* const example2 = cleaned
|
|
49
49
|
* ```
|
|
50
50
|
*/
|
|
51
51
|
export const promiseFinally = async <V>(
|
|
52
|
-
doSomething: () => void,
|
|
53
52
|
target: Promise<V>,
|
|
53
|
+
doSomething: () => void,
|
|
54
54
|
): Promise<V> => {
|
|
55
|
-
return await target.finally(doSomething)
|
|
55
|
+
return await target.finally(() => doSomething())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface PromiseDeferred<V> {
|
|
59
|
+
promise: Promise<V>
|
|
60
|
+
resolve: (value: V) => void
|
|
61
|
+
reject: (reason: unknown) => void
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a deferred promise with external resolve and reject functions.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```
|
|
68
|
+
* const deferred = promiseDeferred<number>()
|
|
69
|
+
* // Expect: typeof deferred.resolve === "function"
|
|
70
|
+
* const example1 = typeof deferred.resolve
|
|
71
|
+
* deferred.resolve(42)
|
|
72
|
+
* const example2 = await deferred.promise
|
|
73
|
+
* // Expect: 42
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export const promiseDeferred = <V>(): PromiseDeferred<V> => {
|
|
77
|
+
let externalResolve!: (value: V) => void
|
|
78
|
+
let externalReject!: (reason: unknown) => void
|
|
79
|
+
const promise = new Promise<V>((resolve, reject) => {
|
|
80
|
+
externalResolve = resolve
|
|
81
|
+
externalReject = reject
|
|
82
|
+
})
|
|
83
|
+
return { promise, resolve: externalResolve, reject: externalReject }
|
|
56
84
|
}
|
|
57
85
|
|
|
58
|
-
const INTERNAL_PROMISE_FAIL_RESULT_TYPE: symbol = Symbol("fail")
|
|
86
|
+
const INTERNAL_PROMISE_FAIL_RESULT_TYPE: unique symbol = Symbol("fail")
|
|
87
|
+
type InternalPromiseFailResultType = typeof INTERNAL_PROMISE_FAIL_RESULT_TYPE
|
|
59
88
|
|
|
60
89
|
/**
|
|
61
90
|
* 表示标准化的 Promise 失败结果。
|
|
62
91
|
*/
|
|
63
92
|
export interface PromiseFailResult {
|
|
64
|
-
__type__:
|
|
93
|
+
__type__: InternalPromiseFailResultType
|
|
65
94
|
reason: unknown
|
|
66
95
|
}
|
|
67
96
|
|
|
@@ -75,14 +104,23 @@ export interface PromiseIndexedFailResult extends PromiseFailResult {
|
|
|
75
104
|
/**
|
|
76
105
|
* 根据拒因构造标准化的 Promise 失败结果。
|
|
77
106
|
*/
|
|
78
|
-
export const
|
|
107
|
+
export const promiseCreateFailResult = (reason: unknown): PromiseFailResult => {
|
|
79
108
|
return { __type__: INTERNAL_PROMISE_FAIL_RESULT_TYPE, reason }
|
|
80
109
|
}
|
|
81
110
|
/**
|
|
82
111
|
* 判断目标值是否为标准化的 Promise 失败结果。
|
|
83
112
|
*/
|
|
84
|
-
export const
|
|
85
|
-
|
|
113
|
+
export const promiseIsFailResult = (target: unknown): target is PromiseFailResult => {
|
|
114
|
+
if (isPlainObject(target) === false) {
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
if ("__type__" in target === false) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
if (target["__type__"] !== INTERNAL_PROMISE_FAIL_RESULT_TYPE) {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
return true
|
|
86
124
|
}
|
|
87
125
|
/**
|
|
88
126
|
* 用作 `Promise.catch` 的 `onrejected` 回调,并返回标准化失败结果。
|
|
@@ -98,9 +136,8 @@ export const promiseFilterSuccessResults = <V>(
|
|
|
98
136
|
results: Array<V | PromiseFailResult>,
|
|
99
137
|
): V[] => {
|
|
100
138
|
const filtered = results.filter(result => {
|
|
101
|
-
return
|
|
139
|
+
return promiseIsFailResult(result) === false
|
|
102
140
|
})
|
|
103
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
104
141
|
return filtered as V[]
|
|
105
142
|
}
|
|
106
143
|
/**
|
|
@@ -112,7 +149,7 @@ export const promiseFilterFailResults = <V>(
|
|
|
112
149
|
const filtered = results.reduce<PromiseIndexedFailResult[]>((
|
|
113
150
|
accumulatedResults, currentResult, index
|
|
114
151
|
) => {
|
|
115
|
-
if (
|
|
152
|
+
if (promiseIsFailResult(currentResult)) {
|
|
116
153
|
accumulatedResults.push({ ...currentResult, index })
|
|
117
154
|
}
|
|
118
155
|
return accumulatedResults
|
|
@@ -131,8 +168,10 @@ interface PromiseQueueRestPromiseMakerContext<V, S = unknown> {
|
|
|
131
168
|
previousResult: V | PromiseFailResult
|
|
132
169
|
state: S
|
|
133
170
|
}
|
|
134
|
-
type PromiseQueueFirstPromiseMaker<T, S = unknown>
|
|
135
|
-
|
|
171
|
+
type PromiseQueueFirstPromiseMaker<T, S = unknown>
|
|
172
|
+
= (context: PromiseQueueFirstPromiseMakerContext<S>) => Promise<T>
|
|
173
|
+
type PromiseQueueRestPromiseMaker<T, S = unknown>
|
|
174
|
+
= (context: PromiseQueueRestPromiseMakerContext<T, S>) => Promise<T>
|
|
136
175
|
type PromiseQueuePromiseMakers<T, S = unknown> = [
|
|
137
176
|
PromiseQueueFirstPromiseMaker<T, S>,
|
|
138
177
|
...Array<PromiseQueueRestPromiseMaker<T, S>>
|
|
@@ -155,7 +194,7 @@ export interface PromiseQueueOptions {
|
|
|
155
194
|
* // Expect: [1, 2]
|
|
156
195
|
* const example1 = await promiseQueue<number>([
|
|
157
196
|
* async () => 1,
|
|
158
|
-
* async ({ previousResult }) => (
|
|
197
|
+
* async ({ previousResult }) => (promiseIsFailResult(previousResult) ? 0 : previousResult + 1),
|
|
159
198
|
* ])
|
|
160
199
|
* ```
|
|
161
200
|
*/
|
|
@@ -169,7 +208,6 @@ export const promiseQueue = async <T, S = unknown>(
|
|
|
169
208
|
let context: PromiseQueueFirstPromiseMakerContext<S> | PromiseQueueRestPromiseMakerContext<T, S> = {
|
|
170
209
|
index: 0,
|
|
171
210
|
hasPreviousResult: false,
|
|
172
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
173
211
|
state: undefined as unknown as S
|
|
174
212
|
}
|
|
175
213
|
|
|
@@ -205,18 +243,20 @@ export const promiseQueue = async <T, S = unknown>(
|
|
|
205
243
|
}
|
|
206
244
|
|
|
207
245
|
interface PromiseRetryFirstPromiseMakerContext<S = unknown> {
|
|
208
|
-
|
|
246
|
+
index: number
|
|
209
247
|
hasPreviousResult: false
|
|
210
248
|
state: S
|
|
211
249
|
}
|
|
212
250
|
interface PromiseRetryRestPromiseMakerContext<T, S = unknown> {
|
|
213
|
-
|
|
251
|
+
index: number
|
|
214
252
|
hasPreviousResult: true
|
|
215
253
|
previousResult: T | PromiseFailResult
|
|
216
254
|
state: S
|
|
217
255
|
}
|
|
218
256
|
type PromiseRetryPromiseMaker<T, S = unknown> = (
|
|
219
|
-
context:
|
|
257
|
+
context:
|
|
258
|
+
| PromiseRetryFirstPromiseMakerContext<S>
|
|
259
|
+
| PromiseRetryRestPromiseMakerContext<T, S>
|
|
220
260
|
) => Promise<T>
|
|
221
261
|
|
|
222
262
|
/**
|
|
@@ -228,9 +268,10 @@ export interface PromiseRetryOptions {
|
|
|
228
268
|
*/
|
|
229
269
|
breakTime?: number
|
|
230
270
|
/**
|
|
271
|
+
* `0` 表示仅执行首次尝试,不进行额外重试。
|
|
231
272
|
* @default Infinity
|
|
232
273
|
*/
|
|
233
|
-
|
|
274
|
+
maxTryIndex?: number | undefined
|
|
234
275
|
}
|
|
235
276
|
/**
|
|
236
277
|
* Retry while the predicate indicates the result is not acceptable.
|
|
@@ -257,34 +298,33 @@ export const promiseRetryWhile = async <T, S = unknown>(
|
|
|
257
298
|
): Promise<T | PromiseFailResult> => {
|
|
258
299
|
const {
|
|
259
300
|
breakTime = 0,
|
|
260
|
-
|
|
301
|
+
maxTryIndex = Infinity,
|
|
261
302
|
} = options ?? {}
|
|
262
303
|
|
|
263
|
-
if (
|
|
264
|
-
throw new Error("`
|
|
304
|
+
if (maxTryIndex < 0) {
|
|
305
|
+
throw new Error("`maxTryIndex` must be greater than or equal to 0.")
|
|
265
306
|
}
|
|
266
307
|
|
|
267
|
-
let
|
|
308
|
+
let index = 0
|
|
268
309
|
// oxlint-disable-next-line no-accumulating-spread
|
|
269
310
|
let context: PromiseRetryFirstPromiseMakerContext<S> | PromiseRetryRestPromiseMakerContext<T, S> = {
|
|
270
|
-
|
|
311
|
+
index,
|
|
271
312
|
hasPreviousResult: false,
|
|
272
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
273
313
|
state: undefined as unknown as S
|
|
274
314
|
}
|
|
275
315
|
let result = await promiseMaker(context).catch(promiseCatchToFailResult)
|
|
276
316
|
|
|
277
317
|
while (
|
|
278
|
-
(
|
|
279
|
-
&&
|
|
318
|
+
(promiseIsFailResult(result) || (await predicate(result)))
|
|
319
|
+
&& index < maxTryIndex
|
|
280
320
|
) {
|
|
281
|
-
|
|
321
|
+
index = index + 1
|
|
282
322
|
// oxlint-disable-next-line no-loop-func
|
|
283
323
|
result = await new Promise<T | PromiseFailResult>((resolve) => {
|
|
284
324
|
setTimeout(() => {
|
|
285
325
|
context = {
|
|
286
326
|
...context,
|
|
287
|
-
|
|
327
|
+
index,
|
|
288
328
|
hasPreviousResult: true,
|
|
289
329
|
previousResult: result,
|
|
290
330
|
}
|
|
@@ -315,40 +355,39 @@ export const promiseRetryWhile = async <T, S = unknown>(
|
|
|
315
355
|
* @see {@link promiseRetryWhile}
|
|
316
356
|
*/
|
|
317
357
|
export const promiseRetryUntil = async <T, S = unknown>(
|
|
318
|
-
predicate: (value: T,
|
|
358
|
+
predicate: (value: T, index: number) => boolean | Promise<boolean>,
|
|
319
359
|
promiseMaker: PromiseRetryPromiseMaker<T, S>,
|
|
320
360
|
options?: PromiseRetryOptions | undefined,
|
|
321
361
|
): Promise<T | PromiseFailResult> => {
|
|
322
362
|
const {
|
|
323
363
|
breakTime = 0,
|
|
324
|
-
|
|
364
|
+
maxTryIndex = Infinity,
|
|
325
365
|
} = options ?? {}
|
|
326
366
|
|
|
327
|
-
if (
|
|
328
|
-
throw new Error("`
|
|
367
|
+
if (maxTryIndex < 0) {
|
|
368
|
+
throw new Error("`maxTryIndex` must be greater than or equal to 0.")
|
|
329
369
|
}
|
|
330
370
|
|
|
331
|
-
let
|
|
371
|
+
let index = 0
|
|
332
372
|
// oxlint-disable-next-line no-accumulating-spread
|
|
333
373
|
let context: PromiseRetryFirstPromiseMakerContext<S> | PromiseRetryRestPromiseMakerContext<T, S> = {
|
|
334
|
-
|
|
374
|
+
index,
|
|
335
375
|
hasPreviousResult: false,
|
|
336
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
337
376
|
state: undefined as unknown as S
|
|
338
377
|
}
|
|
339
378
|
let result = await promiseMaker(context).catch(promiseCatchToFailResult)
|
|
340
379
|
|
|
341
380
|
while (
|
|
342
|
-
(
|
|
343
|
-
&&
|
|
381
|
+
(promiseIsFailResult(result) || !(await predicate(result, index)))
|
|
382
|
+
&& index < maxTryIndex
|
|
344
383
|
) {
|
|
345
|
-
|
|
384
|
+
index = index + 1
|
|
346
385
|
// oxlint-disable-next-line no-loop-func
|
|
347
386
|
result = await new Promise<T | PromiseFailResult>((resolve) => {
|
|
348
387
|
setTimeout(() => {
|
|
349
388
|
context = {
|
|
350
389
|
...context,
|
|
351
|
-
|
|
390
|
+
index,
|
|
352
391
|
hasPreviousResult: true,
|
|
353
392
|
previousResult: result
|
|
354
393
|
}
|
|
@@ -362,13 +401,14 @@ export const promiseRetryUntil = async <T, S = unknown>(
|
|
|
362
401
|
|
|
363
402
|
/**
|
|
364
403
|
* Start periodic asynchronous execution and return a function to stop it.
|
|
404
|
+
* Rejected runs are logged and ignored to avoid unhandled promise rejections.
|
|
365
405
|
*
|
|
366
406
|
* @example
|
|
367
407
|
* ```
|
|
368
408
|
* const records: number[] = []
|
|
369
|
-
* const stop = promiseInterval(10, async (
|
|
370
|
-
* records.push(
|
|
371
|
-
* return
|
|
409
|
+
* const stop = promiseInterval(10, async (index) => {
|
|
410
|
+
* records.push(index)
|
|
411
|
+
* return index
|
|
372
412
|
* })
|
|
373
413
|
* // Later: stop()
|
|
374
414
|
* // Expect: typeof stop === "function"
|
|
@@ -377,13 +417,15 @@ export const promiseRetryUntil = async <T, S = unknown>(
|
|
|
377
417
|
*/
|
|
378
418
|
export const promiseInterval = <T>(
|
|
379
419
|
interval: number,
|
|
380
|
-
promiseMaker: (
|
|
420
|
+
promiseMaker: (index: number) => Promise<T>,
|
|
381
421
|
): (() => void) => {
|
|
382
|
-
let
|
|
422
|
+
let index = 0
|
|
383
423
|
|
|
384
424
|
const intervalID = setInterval(() => {
|
|
385
|
-
void promiseMaker(
|
|
386
|
-
|
|
425
|
+
void promiseMaker(index).catch((error: unknown) => {
|
|
426
|
+
console.error("[promiseInterval] unexpected error occurred:", error)
|
|
427
|
+
})
|
|
428
|
+
index = index + 1
|
|
387
429
|
}, interval)
|
|
388
430
|
|
|
389
431
|
return () => {
|
|
@@ -392,18 +434,20 @@ export const promiseInterval = <T>(
|
|
|
392
434
|
}
|
|
393
435
|
|
|
394
436
|
interface PromiseForeverFirstPromiseMakerContext<S = unknown> {
|
|
395
|
-
|
|
437
|
+
index: number
|
|
396
438
|
hasPreviousResult: false
|
|
397
439
|
state: S
|
|
398
440
|
}
|
|
399
441
|
interface PromiseForeverRestPromiseMakerContext<T, S = unknown> {
|
|
400
|
-
|
|
442
|
+
index: number
|
|
401
443
|
hasPreviousResult: true
|
|
402
444
|
previousResult: T | PromiseFailResult
|
|
403
445
|
state: S
|
|
404
446
|
}
|
|
405
447
|
type PromiseForeverPromiseMaker<T, S = unknown> = (
|
|
406
|
-
context:
|
|
448
|
+
context:
|
|
449
|
+
| PromiseForeverFirstPromiseMakerContext<S>
|
|
450
|
+
| PromiseForeverRestPromiseMakerContext<T, S>
|
|
407
451
|
) => Promise<T>
|
|
408
452
|
|
|
409
453
|
/**
|
|
@@ -414,60 +458,80 @@ export interface PromiseForeverOptions {
|
|
|
414
458
|
* @default 0
|
|
415
459
|
*/
|
|
416
460
|
breakTime?: number | undefined
|
|
417
|
-
onRejected?: ((
|
|
461
|
+
onRejected?: ((index: number, result: PromiseFailResult) => void) | undefined
|
|
462
|
+
}
|
|
463
|
+
export interface PromiseForeverResult {
|
|
464
|
+
index: number
|
|
465
|
+
isStopped: boolean
|
|
466
|
+
stop: () => void
|
|
418
467
|
}
|
|
419
468
|
/**
|
|
420
469
|
* Continuously execute an async maker forever with optional delay and rejection hook.
|
|
421
470
|
*
|
|
471
|
+
* - 至少会执行一次,`stop()` 可以用来提前停止后续执行。
|
|
472
|
+
* - `index` 从 `0` 开始,表示当前执行轮次。
|
|
473
|
+
* - 如果某一轮结束后已经排入了下一轮的定时器,调用 `stop()` 不会取消那一轮;
|
|
474
|
+
* 它只会阻止后续继续排入新的轮次。
|
|
475
|
+
*
|
|
422
476
|
* @example
|
|
423
477
|
* ```
|
|
424
478
|
* let times = 0
|
|
425
|
-
* promiseForever(async () => {
|
|
479
|
+
* const controller = promiseForever(async () => {
|
|
426
480
|
* times = times + 1
|
|
427
481
|
* return times
|
|
428
482
|
* }, { breakTime: 0 })
|
|
429
|
-
*
|
|
430
|
-
*
|
|
483
|
+
* controller.stop()
|
|
484
|
+
* // Expect: typeof controller.stop === "function"
|
|
485
|
+
* const example1 = typeof controller.stop
|
|
431
486
|
* ```
|
|
432
487
|
*/
|
|
433
488
|
export const promiseForever = <T, S = unknown>(
|
|
434
489
|
promiseMaker: PromiseForeverPromiseMaker<T, S>,
|
|
435
490
|
options?: PromiseForeverOptions | undefined,
|
|
436
|
-
):
|
|
491
|
+
): PromiseForeverResult => {
|
|
437
492
|
const {
|
|
438
493
|
breakTime = 0,
|
|
439
494
|
onRejected,
|
|
440
495
|
} = options ?? {}
|
|
441
496
|
|
|
442
|
-
let
|
|
497
|
+
let index = 0
|
|
443
498
|
let context: PromiseForeverFirstPromiseMakerContext<S> | PromiseForeverRestPromiseMakerContext<T, S> = {
|
|
444
|
-
|
|
499
|
+
index,
|
|
445
500
|
hasPreviousResult: false,
|
|
446
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
447
501
|
state: undefined as unknown as S
|
|
448
502
|
}
|
|
503
|
+
const foreverResult: PromiseForeverResult = {
|
|
504
|
+
index,
|
|
505
|
+
isStopped: false,
|
|
506
|
+
stop: () => {
|
|
507
|
+
foreverResult.isStopped = true
|
|
508
|
+
}
|
|
509
|
+
}
|
|
449
510
|
|
|
450
511
|
const handlePromise = (result: T | PromiseFailResult): void => {
|
|
451
|
-
time = time + 1
|
|
452
512
|
setTimeout(() => {
|
|
513
|
+
index = index + 1
|
|
453
514
|
context = {
|
|
454
515
|
...context,
|
|
455
|
-
|
|
516
|
+
index,
|
|
456
517
|
hasPreviousResult: true,
|
|
457
518
|
previousResult: result
|
|
458
519
|
}
|
|
520
|
+
foreverResult.index = index
|
|
459
521
|
runPromise(context)
|
|
460
522
|
}, breakTime)
|
|
461
523
|
}
|
|
462
524
|
const runPromise = (
|
|
463
|
-
context:
|
|
525
|
+
context:
|
|
526
|
+
| PromiseForeverFirstPromiseMakerContext<S>
|
|
527
|
+
| PromiseForeverRestPromiseMakerContext<T, S>
|
|
464
528
|
): void => {
|
|
465
529
|
void promiseMaker(context)
|
|
466
530
|
.catch((error: unknown) => {
|
|
467
531
|
const failedResult = promiseCatchToFailResult(error)
|
|
468
532
|
if (onRejected !== undefined) {
|
|
469
533
|
try {
|
|
470
|
-
onRejected(
|
|
534
|
+
onRejected(index, failedResult)
|
|
471
535
|
}
|
|
472
536
|
catch {
|
|
473
537
|
// omit exceptions from execution of `onRejected`
|
|
@@ -478,8 +542,14 @@ export const promiseForever = <T, S = unknown>(
|
|
|
478
542
|
}
|
|
479
543
|
return failedResult
|
|
480
544
|
})
|
|
481
|
-
.then(result =>
|
|
545
|
+
.then(result => {
|
|
546
|
+
if (foreverResult.isStopped === false) {
|
|
547
|
+
handlePromise(result)
|
|
548
|
+
}
|
|
549
|
+
})
|
|
482
550
|
}
|
|
483
551
|
|
|
484
552
|
runPromise(context)
|
|
553
|
+
|
|
554
|
+
return foreverResult
|
|
485
555
|
}
|
|
@@ -70,7 +70,6 @@ export const decodeCursor = <Model, Key extends keyof Model = keyof Model>(
|
|
|
70
70
|
const key = orderBy[index]!.key
|
|
71
71
|
return { ...acc, [key]: value }
|
|
72
72
|
}, {})
|
|
73
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
74
73
|
return decoded as DecodedCursor<Model, Key>
|
|
75
74
|
}
|
|
76
75
|
|
|
@@ -164,7 +163,6 @@ export const buildCursorBasedPagination = <Table extends AnyPgTableWithColumns,
|
|
|
164
163
|
type Model = InferModelFromPgTableWithColumns<Table>
|
|
165
164
|
|
|
166
165
|
const decodeCursorResult = decodeCursor({
|
|
167
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
168
166
|
modelForType: {} as Model,
|
|
169
167
|
cursor,
|
|
170
168
|
orderBy,
|
|
@@ -66,7 +66,6 @@ export class ClassEventProxy<Target, Events extends EventsConstraint<Events>> {
|
|
|
66
66
|
|
|
67
67
|
if (proxySubscriberEntry === undefined) {
|
|
68
68
|
const managedSubscribers = new Map<Events[K], SubscriberEntry<Events, K>>()
|
|
69
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
70
69
|
const proxySubscriber = ((...args: Parameters<Events[K]>) => {
|
|
71
70
|
const managedSubscriberEntries = [...managedSubscribers.values()]
|
|
72
71
|
for (const managedSubscriberEntry of managedSubscriberEntries) {
|
|
@@ -196,7 +195,6 @@ export class ClassEventProxy<Target, Events extends EventsConstraint<Events>> {
|
|
|
196
195
|
|
|
197
196
|
const events = Object.keys(proxySubscriberEntryMap)
|
|
198
197
|
for (const event of events) {
|
|
199
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
200
198
|
this.removeSubscribersOfEvent(target, event as keyof Events)
|
|
201
199
|
}
|
|
202
200
|
this.subscribers.delete(target)
|
|
@@ -58,7 +58,6 @@ export class InstanceEventProxy<Events extends EventsConstraint<Events>> {
|
|
|
58
58
|
|
|
59
59
|
if (proxySubscriberEntry === undefined) {
|
|
60
60
|
const managedSubscribers = new Map<Events[K], SubscriberEntry<Events, K>>()
|
|
61
|
-
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
62
61
|
const proxySubscriber = ((...args: Parameters<Events[K]>) => {
|
|
63
62
|
const managedSubscriberEntries = [...managedSubscribers.values()]
|
|
64
63
|
for (const managedSubscriberEntry of managedSubscriberEntries) {
|
|
@@ -168,7 +167,6 @@ export class InstanceEventProxy<Events extends EventsConstraint<Events>> {
|
|
|
168
167
|
const events = Object.keys(this.subscribers)
|
|
169
168
|
|
|
170
169
|
for (const event of events) {
|
|
171
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
172
170
|
this.removeSubscribersOfEvent(event as keyof Events)
|
|
173
171
|
}
|
|
174
172
|
this.subscribers = {}
|