@planet-matrix/mobius-model 0.6.0 → 0.9.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/CHANGELOG.md +37 -0
- package/dist/index.js +706 -36
- package/dist/index.js.map +855 -59
- package/package.json +28 -16
- package/src/ai/README.md +1 -0
- package/src/ai/ai.ts +107 -0
- package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
- package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
- package/src/ai/chat-completion-ai/index.ts +7 -0
- package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
- package/src/ai/embedding-ai/embedding-ai.ts +63 -0
- package/src/ai/embedding-ai/embedding.ts +50 -0
- package/src/ai/embedding-ai/index.ts +4 -0
- package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
- package/src/ai/index.ts +4 -0
- package/src/aio/README.md +100 -0
- package/src/aio/content.ts +141 -0
- package/src/aio/index.ts +3 -0
- package/src/aio/json.ts +127 -0
- package/src/aio/prompt.ts +246 -0
- package/src/basic/README.md +20 -15
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +2 -2
- package/src/basic/index.ts +1 -0
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- package/src/credential/README.md +107 -0
- package/src/credential/api-key.ts +158 -0
- package/src/credential/bearer.ts +73 -0
- package/src/credential/index.ts +4 -0
- package/src/credential/json-web-token.ts +96 -0
- package/src/credential/password.ts +170 -0
- package/src/cron/README.md +86 -0
- package/src/cron/cron.ts +87 -0
- package/src/cron/index.ts +1 -0
- package/src/drizzle/README.md +1 -0
- package/src/drizzle/drizzle.ts +1 -0
- package/src/drizzle/helper.ts +47 -0
- package/src/drizzle/index.ts +5 -0
- package/src/drizzle/infer.ts +52 -0
- package/src/drizzle/kysely.ts +8 -0
- package/src/drizzle/pagination.ts +200 -0
- package/src/email/README.md +1 -0
- package/src/email/index.ts +1 -0
- package/src/email/resend.ts +25 -0
- package/src/event/class-event-proxy.ts +6 -5
- package/src/event/common.ts +13 -3
- package/src/event/event-manager.ts +3 -3
- package/src/event/instance-event-proxy.ts +6 -5
- package/src/event/internal.ts +4 -4
- package/src/form/README.md +25 -0
- package/src/form/index.ts +1 -0
- package/src/form/inputor-controller/base.ts +874 -0
- package/src/form/inputor-controller/boolean.ts +39 -0
- package/src/form/inputor-controller/file.ts +39 -0
- package/src/form/inputor-controller/form.ts +181 -0
- package/src/form/inputor-controller/helper.ts +117 -0
- package/src/form/inputor-controller/index.ts +17 -0
- package/src/form/inputor-controller/multi-select.ts +99 -0
- package/src/form/inputor-controller/number.ts +116 -0
- package/src/form/inputor-controller/select.ts +109 -0
- package/src/form/inputor-controller/text.ts +82 -0
- package/src/http/READMD.md +1 -0
- package/src/http/api/api-core.ts +84 -0
- package/src/http/api/api-handler.ts +79 -0
- package/src/http/api/api-host.ts +47 -0
- package/src/http/api/api-result.ts +56 -0
- package/src/http/api/api-schema.ts +154 -0
- package/src/http/api/api-server.ts +130 -0
- package/src/http/api/api-test.ts +142 -0
- package/src/http/api/api-type.ts +37 -0
- package/src/http/api/api.ts +81 -0
- package/src/http/api/index.ts +11 -0
- package/src/http/api-adapter/api-core-node-http.ts +260 -0
- package/src/http/api-adapter/api-host-node-http.ts +156 -0
- package/src/http/api-adapter/api-result-arktype.ts +297 -0
- package/src/http/api-adapter/api-result-zod.ts +286 -0
- package/src/http/api-adapter/index.ts +5 -0
- package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
- package/src/http/bin/gen-api-list/index.ts +1 -0
- package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
- package/src/http/bin/gen-api-test/index.ts +1 -0
- package/src/http/bin/gen-api-type/calc-code.ts +25 -0
- package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
- package/src/http/bin/gen-api-type/index.ts +2 -0
- package/src/http/bin/index.ts +2 -0
- package/src/http/index.ts +3 -0
- package/src/huawei/README.md +1 -0
- package/src/huawei/index.ts +2 -0
- package/src/huawei/moderation/index.ts +1 -0
- package/src/huawei/moderation/moderation.ts +355 -0
- package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
- package/src/huawei/obs/index.ts +1 -0
- package/src/huawei/obs/obs.ts +42 -0
- package/src/index.ts +19 -2
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/logger.ts +15 -4
- package/src/openai/README.md +1 -0
- package/src/openai/index.ts +1 -0
- package/src/openai/openai.ts +510 -0
- package/src/orchestration/README.md +9 -7
- package/src/orchestration/dispatching/dispatcher.ts +83 -0
- package/src/orchestration/dispatching/index.ts +2 -0
- package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
- package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
- package/src/orchestration/dispatching/selector/index.ts +2 -0
- package/src/orchestration/index.ts +2 -0
- package/src/orchestration/scheduling/index.ts +2 -0
- package/src/orchestration/scheduling/scheduler.ts +103 -0
- package/src/orchestration/scheduling/task.ts +32 -0
- package/src/random/README.md +8 -7
- package/src/random/base.ts +66 -0
- package/src/random/index.ts +5 -1
- package/src/random/random-boolean.ts +40 -0
- package/src/random/random-integer.ts +60 -0
- package/src/random/random-number.ts +72 -0
- package/src/random/random-string.ts +66 -0
- package/src/request/README.md +108 -0
- package/src/request/fetch/base.ts +108 -0
- package/src/request/fetch/browser.ts +285 -0
- package/src/request/fetch/general.ts +20 -0
- package/src/request/fetch/index.ts +4 -0
- package/src/request/fetch/nodejs.ts +285 -0
- package/src/request/index.ts +2 -0
- package/src/request/request/base.ts +250 -0
- package/src/request/request/general.ts +64 -0
- package/src/request/request/index.ts +3 -0
- package/src/request/request/resource.ts +68 -0
- package/src/result/README.md +4 -0
- package/src/result/controller.ts +54 -0
- package/src/result/either.ts +193 -0
- package/src/result/index.ts +2 -0
- package/src/route/README.md +105 -0
- package/src/route/adapter/browser.ts +122 -0
- package/src/route/adapter/driver.ts +56 -0
- package/src/route/adapter/index.ts +2 -0
- package/src/route/index.ts +3 -0
- package/src/route/router/index.ts +2 -0
- package/src/route/router/route.ts +630 -0
- package/src/route/router/router.ts +1642 -0
- package/src/route/uri/hash.ts +308 -0
- package/src/route/uri/index.ts +7 -0
- package/src/route/uri/pathname.ts +376 -0
- package/src/route/uri/search.ts +413 -0
- package/src/socket/README.md +105 -0
- package/src/socket/client/index.ts +2 -0
- package/src/socket/client/socket-unit.ts +660 -0
- package/src/socket/client/socket.ts +203 -0
- package/src/socket/common/index.ts +2 -0
- package/src/socket/common/socket-unit-common.ts +23 -0
- package/src/socket/common/socket-unit-heartbeat.ts +427 -0
- package/src/socket/index.ts +3 -0
- package/src/socket/server/index.ts +3 -0
- package/src/socket/server/server.ts +183 -0
- package/src/socket/server/socket-unit.ts +449 -0
- package/src/socket/server/socket.ts +264 -0
- package/src/storage/table.ts +3 -3
- package/src/timer/expiration/expiration-manager.ts +3 -3
- package/src/timer/expiration/remaining-manager.ts +3 -3
- package/src/tube/README.md +99 -0
- package/src/tube/helper.ts +138 -0
- package/src/tube/index.ts +2 -0
- package/src/tube/tube.ts +880 -0
- package/src/weixin/README.md +1 -0
- package/src/weixin/index.ts +2 -0
- package/src/weixin/official-account/authorization.ts +159 -0
- package/src/weixin/official-account/index.ts +2 -0
- package/src/weixin/official-account/js-api.ts +134 -0
- package/src/weixin/open/index.ts +1 -0
- package/src/weixin/open/oauth2.ts +133 -0
- package/tests/unit/ai/ai.spec.ts +85 -0
- package/tests/unit/aio/content.spec.ts +105 -0
- package/tests/unit/aio/json.spec.ts +147 -0
- package/tests/unit/aio/prompt.spec.ts +111 -0
- package/tests/unit/basic/error.spec.ts +16 -4
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +90 -37
- package/tests/unit/credential/api-key.spec.ts +37 -0
- package/tests/unit/credential/bearer.spec.ts +23 -0
- package/tests/unit/credential/json-web-token.spec.ts +23 -0
- package/tests/unit/credential/password.spec.ts +41 -0
- package/tests/unit/cron/cron.spec.ts +84 -0
- package/tests/unit/event/class-event-proxy.spec.ts +3 -3
- package/tests/unit/event/event-manager.spec.ts +3 -3
- package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
- package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
- package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
- package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
- package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
- package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
- package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
- package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
- package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
- package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
- package/tests/unit/http/api/api-core-host.spec.ts +207 -0
- package/tests/unit/http/api/api-schema.spec.ts +120 -0
- package/tests/unit/http/api/api-server.spec.ts +363 -0
- package/tests/unit/http/api/api-test.spec.ts +117 -0
- package/tests/unit/http/api/api.spec.ts +121 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
- package/tests/unit/json/repair.spec.ts +11 -0
- package/tests/unit/log/logger.spec.ts +19 -4
- package/tests/unit/openai/openai.spec.ts +64 -0
- package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
- package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
- package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
- package/tests/unit/random/base.spec.ts +58 -0
- package/tests/unit/random/random-boolean.spec.ts +25 -0
- package/tests/unit/random/random-integer.spec.ts +32 -0
- package/tests/unit/random/random-number.spec.ts +33 -0
- package/tests/unit/random/random-string.spec.ts +22 -0
- package/tests/unit/request/fetch/browser.spec.ts +222 -0
- package/tests/unit/request/fetch/general.spec.ts +43 -0
- package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
- package/tests/unit/request/request/base.spec.ts +385 -0
- package/tests/unit/request/request/general.spec.ts +161 -0
- package/tests/unit/route/router/route.spec.ts +431 -0
- package/tests/unit/route/router/router.spec.ts +407 -0
- package/tests/unit/route/uri/hash.spec.ts +72 -0
- package/tests/unit/route/uri/pathname.spec.ts +147 -0
- package/tests/unit/route/uri/search.spec.ts +107 -0
- package/tests/unit/socket/client.spec.ts +208 -0
- package/tests/unit/socket/server.spec.ts +135 -0
- package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
- package/tests/unit/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/src/random/string.ts +0 -35
- package/tests/unit/random/string.spec.ts +0 -11
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Drizzle
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as d from "drizzle-orm"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ExtractTablesWithRelations } from "drizzle-orm"
|
|
2
|
+
import { PgDatabase, PgTransaction } from "drizzle-orm/pg-core"
|
|
3
|
+
import type {
|
|
4
|
+
PostgresJsDatabase as RawPostgresJsDatabase,
|
|
5
|
+
PostgresJsTransaction as RawPostgresJsTransaction,
|
|
6
|
+
} from "drizzle-orm/postgres-js"
|
|
7
|
+
|
|
8
|
+
export type PostgresJsNormalDatabase<Schema extends Record<string, unknown> = Record<string, never>>
|
|
9
|
+
= RawPostgresJsDatabase<Schema>
|
|
10
|
+
export type PostgresJsTransactionDatabase<Schema extends Record<string, unknown> = Record<string, never>>
|
|
11
|
+
= RawPostgresJsTransaction<Schema, ExtractTablesWithRelations<Schema>>
|
|
12
|
+
export type PostgresJsDatabase<Schema extends Record<string, unknown> = Record<string, never>>
|
|
13
|
+
= PostgresJsNormalDatabase<Schema> | PostgresJsTransactionDatabase<Schema>
|
|
14
|
+
export interface WithPostgresJsDatabase<Schema extends Record<string, unknown> = Record<string, never>> {
|
|
15
|
+
database: PostgresJsDatabase<Schema>
|
|
16
|
+
}
|
|
17
|
+
export interface WithOptionalPostgresJsDatabase<Schema extends Record<string, unknown> = Record<string, never>> {
|
|
18
|
+
database?: PostgresJsDatabase<Schema> | undefined
|
|
19
|
+
}
|
|
20
|
+
export type WithoutPostgresJsDatabase<T> = Omit<T, "database">
|
|
21
|
+
|
|
22
|
+
export const isPostgresJsNormalDatabase = <Schema extends Record<string, unknown> = Record<string, never>>(
|
|
23
|
+
database: PostgresJsDatabase<Schema>,
|
|
24
|
+
): database is PostgresJsNormalDatabase<Schema> => {
|
|
25
|
+
return database instanceof PgDatabase
|
|
26
|
+
}
|
|
27
|
+
export const isPostgresJsTransactionDatabase = <Schema extends Record<string, unknown> = Record<string, never>>(
|
|
28
|
+
database: PostgresJsDatabase<Schema>,
|
|
29
|
+
): database is PostgresJsTransactionDatabase<Schema> => {
|
|
30
|
+
return database instanceof PgTransaction
|
|
31
|
+
}
|
|
32
|
+
export const autoPostgresJsDatabaseTransaction = async <Result, Schema extends Record<string, unknown> = Record<string, never>>(
|
|
33
|
+
database: PostgresJsDatabase<Schema>,
|
|
34
|
+
run: (database: PostgresJsTransactionDatabase<Schema>) => Promise<Result>,
|
|
35
|
+
): Promise<Result> => {
|
|
36
|
+
// detect transaction database first, then normal database
|
|
37
|
+
// because transaction database is a subclass of normal database
|
|
38
|
+
if (isPostgresJsTransactionDatabase(database)) {
|
|
39
|
+
return await run(database)
|
|
40
|
+
}
|
|
41
|
+
if (isPostgresJsNormalDatabase(database)) {
|
|
42
|
+
return await database.transaction(async (database) => {
|
|
43
|
+
return await run(database)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
throw new Error("Invalid database instance")
|
|
47
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DrizzleTypeError, InferEnum, InferInsertModel, InferSelectModel } from "drizzle-orm"
|
|
2
|
+
import type { PgEnum, PgTableWithColumns } from "drizzle-orm/pg-core"
|
|
3
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"
|
|
4
|
+
|
|
5
|
+
type RemoveNeverValue<T> = {
|
|
6
|
+
[K in keyof T as T[K] extends never ? never : K]: T[K]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type PreInferModel<T extends Record<string, unknown> = Record<string, never>> = {
|
|
10
|
+
[K in keyof T]: T[K] extends PgTableWithColumns<infer _>
|
|
11
|
+
? InferSelectModel<T[K]>
|
|
12
|
+
: (
|
|
13
|
+
T[K] extends PgEnum<infer __>
|
|
14
|
+
? InferEnum<T[K]>
|
|
15
|
+
: never
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
export type InferModel<T extends Record<string, unknown> = Record<string, never>> = RemoveNeverValue<PreInferModel<T>>
|
|
19
|
+
|
|
20
|
+
type PreInferInsert<T extends Record<string, unknown> = Record<string, never>> = {
|
|
21
|
+
[K in keyof T]: T[K] extends PgTableWithColumns<infer _>
|
|
22
|
+
? InferInsertModel<T[K]>
|
|
23
|
+
: never
|
|
24
|
+
}
|
|
25
|
+
export type InferInsert<T extends Record<string, unknown> = Record<string, never>> = RemoveNeverValue<PreInferInsert<T>>
|
|
26
|
+
|
|
27
|
+
type PreInferUpdate<T extends Record<string, unknown> = Record<string, never>> = {
|
|
28
|
+
[K in keyof T]: T[K] extends PgTableWithColumns<infer _>
|
|
29
|
+
? Partial<InferSelectModel<T[K]>>
|
|
30
|
+
: (
|
|
31
|
+
T[K] extends PgEnum<infer __>
|
|
32
|
+
? InferEnum<T[K]>
|
|
33
|
+
: never
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
export type InferUpdate<T extends Record<string, unknown> = Record<string, never>> = RemoveNeverValue<PreInferUpdate<T>>
|
|
37
|
+
|
|
38
|
+
type GetValidQuery<T extends Record<string, unknown> = Record<string, never>> = Exclude<
|
|
39
|
+
PostgresJsDatabase<T>["query"],
|
|
40
|
+
DrizzleTypeError<string>
|
|
41
|
+
>
|
|
42
|
+
type PreInferWith<T extends Record<string, unknown> = Record<string, never>> = {
|
|
43
|
+
[K in keyof GetValidQuery<T>]: NonNullable<NonNullable<Parameters<GetValidQuery<T>[K]["findMany"]>[0]>["with"]>
|
|
44
|
+
}
|
|
45
|
+
export type InferWith<T extends Record<string, unknown> = Record<string, never>> = RemoveNeverValue<PreInferWith<T>>
|
|
46
|
+
|
|
47
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
48
|
+
export type AnyPgTableWithColumns = PgTableWithColumns<any>
|
|
49
|
+
type PreInferModelFromPgTableWithColumns<T extends AnyPgTableWithColumns> = {
|
|
50
|
+
[K in keyof InferSelectModel<T>]: InferSelectModel<T>[K]
|
|
51
|
+
}
|
|
52
|
+
export type InferModelFromPgTableWithColumns<T extends AnyPgTableWithColumns> = RemoveNeverValue<PreInferModelFromPgTableWithColumns<T>>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Table } from "drizzle-orm"
|
|
2
|
+
import type { Kyselify } from "drizzle-orm/kysely"
|
|
3
|
+
import type { AnyPgTable } from "drizzle-orm/pg-core"
|
|
4
|
+
|
|
5
|
+
export type KyselifyTable<T extends Table> = Kyselify<T>
|
|
6
|
+
export type KyselifyDatabase<T extends Record<string, unknown> = Record<string, never>> = {
|
|
7
|
+
[K in keyof T as T[K] extends AnyPgTable ? T[K]["_"]["name"] : never]: T[K] extends AnyPgTable ? Kyselify<T[K]> : never
|
|
8
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { d } from "./drizzle.ts"
|
|
2
|
+
import type { AnyPgTableWithColumns, InferModelFromPgTableWithColumns } from "./infer.ts"
|
|
3
|
+
|
|
4
|
+
export type Cursor = string | null
|
|
5
|
+
export interface WithCursorBasedPaginationOptions {
|
|
6
|
+
limit: number
|
|
7
|
+
cursor: Cursor
|
|
8
|
+
}
|
|
9
|
+
export interface WithCursorBasedPaginationResult {
|
|
10
|
+
nextCursor: Cursor
|
|
11
|
+
totalCount: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface OrderByItem<Key> {
|
|
15
|
+
key: Key
|
|
16
|
+
order: "asc" | "desc"
|
|
17
|
+
}
|
|
18
|
+
export interface EncodeCursorOptions<Model> {
|
|
19
|
+
model: Model
|
|
20
|
+
orderBy: Array<OrderByItem<keyof Model>>
|
|
21
|
+
}
|
|
22
|
+
export type EncodeCursorResult = string
|
|
23
|
+
export const encodeCursor = <Model>(
|
|
24
|
+
options: EncodeCursorOptions<Model>,
|
|
25
|
+
): EncodeCursorResult => {
|
|
26
|
+
const { model, orderBy } = options
|
|
27
|
+
const encoded = orderBy
|
|
28
|
+
.map((item) => {
|
|
29
|
+
const { key } = item
|
|
30
|
+
if (model[key] === undefined) {
|
|
31
|
+
throw new Error(`model[${String(key)}] is undefined`)
|
|
32
|
+
}
|
|
33
|
+
return model[key]
|
|
34
|
+
})
|
|
35
|
+
.map((value) => {
|
|
36
|
+
if (value instanceof Date) {
|
|
37
|
+
return `Date(${value.getTime()})`
|
|
38
|
+
}
|
|
39
|
+
return String(value)
|
|
40
|
+
})
|
|
41
|
+
.join(":")
|
|
42
|
+
return btoa(encoded)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DecodeCursorOptions<Model, Key extends keyof Model = keyof Model> {
|
|
46
|
+
modelForType: Model
|
|
47
|
+
cursor: Cursor
|
|
48
|
+
orderBy: Array<OrderByItem<Key>>
|
|
49
|
+
}
|
|
50
|
+
export type DecodedCursor<Model, Key extends keyof Model = keyof Model> = {
|
|
51
|
+
[K in Key]: Model[K] extends Date ? Date : Model[K]
|
|
52
|
+
}
|
|
53
|
+
export type DecodeCursorResult<Model, Key extends keyof Model = keyof Model> = DecodedCursor<Model, Key> | null
|
|
54
|
+
export const decodeCursor = <Model, Key extends keyof Model = keyof Model>(
|
|
55
|
+
options: DecodeCursorOptions<Model, Key>,
|
|
56
|
+
): DecodeCursorResult<Model, Key> => {
|
|
57
|
+
const { cursor, orderBy } = options
|
|
58
|
+
if (cursor === null) {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
const decoded = atob(cursor)
|
|
62
|
+
.split(":")
|
|
63
|
+
.map((value) => {
|
|
64
|
+
if (value.startsWith("Date(")) {
|
|
65
|
+
return new Date(Number.parseInt(value.slice(5, -1), 10))
|
|
66
|
+
}
|
|
67
|
+
return value
|
|
68
|
+
})
|
|
69
|
+
.reduce((acc, value, index) => {
|
|
70
|
+
const key = orderBy[index]!.key
|
|
71
|
+
return { ...acc, [key]: value }
|
|
72
|
+
}, {})
|
|
73
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
74
|
+
return decoded as DecodedCursor<Model, Key>
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface GetNextCursorOptions<Model> {
|
|
78
|
+
list: Model[]
|
|
79
|
+
limit: number
|
|
80
|
+
encodeOptions: Omit<EncodeCursorOptions<Model>, "model">
|
|
81
|
+
}
|
|
82
|
+
export const getNextCursor = <Model>(options: GetNextCursorOptions<Model>): Cursor => {
|
|
83
|
+
const { list, limit, encodeOptions } = options
|
|
84
|
+
if (list.length === 0) {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
if (list.length < limit) {
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
const lastModel = list.at(-1)!
|
|
91
|
+
const cursor = encodeCursor({
|
|
92
|
+
model: lastModel,
|
|
93
|
+
...encodeOptions,
|
|
94
|
+
})
|
|
95
|
+
return cursor
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface BuildWhereSqlOptions<Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>> {
|
|
99
|
+
table: Table
|
|
100
|
+
orderBy: Array<OrderByItem<Key>>
|
|
101
|
+
decodeCursorResult: DecodeCursorResult<InferModelFromPgTableWithColumns<Table>, Key>
|
|
102
|
+
}
|
|
103
|
+
export const buildWhereSql = <Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>>(
|
|
104
|
+
options: BuildWhereSqlOptions<Table, Key>,
|
|
105
|
+
): d.SQL | undefined => {
|
|
106
|
+
const { table, decodeCursorResult, orderBy } = options
|
|
107
|
+
if (decodeCursorResult === null) {
|
|
108
|
+
return undefined
|
|
109
|
+
}
|
|
110
|
+
if (orderBy.length === 0) {
|
|
111
|
+
return undefined
|
|
112
|
+
}
|
|
113
|
+
const conditionSqlList = orderBy.map((item) => {
|
|
114
|
+
const { key, order } = item
|
|
115
|
+
if (order === "asc") {
|
|
116
|
+
return d.gt(table[key], decodeCursorResult[key])
|
|
117
|
+
}
|
|
118
|
+
if (order === "desc") {
|
|
119
|
+
return d.lt(table[key], decodeCursorResult[key])
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Invalid order: ${String(order)}`)
|
|
122
|
+
})
|
|
123
|
+
return d.and(...conditionSqlList)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface BuildOrderBySql<Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>> {
|
|
127
|
+
table: Table
|
|
128
|
+
orderBy: Array<OrderByItem<Key>>
|
|
129
|
+
}
|
|
130
|
+
export const buildOrderBySql = <Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>>(
|
|
131
|
+
options: BuildOrderBySql<Table, Key>,
|
|
132
|
+
): d.SQL[] => {
|
|
133
|
+
const { table, orderBy } = options
|
|
134
|
+
const sqlList = orderBy.map((item) => {
|
|
135
|
+
const { key, order } = item
|
|
136
|
+
if (order === "asc") {
|
|
137
|
+
return d.asc(table[key])
|
|
138
|
+
}
|
|
139
|
+
if (order === "desc") {
|
|
140
|
+
return d.desc(table[key])
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`Invalid order: ${String(order)}`)
|
|
143
|
+
})
|
|
144
|
+
return sqlList
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface BuildCursorBasedPaginationOptions<Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>> {
|
|
148
|
+
table: Table
|
|
149
|
+
orderBy: Array<OrderByItem<Key>>
|
|
150
|
+
cursor: Cursor
|
|
151
|
+
limit: number
|
|
152
|
+
}
|
|
153
|
+
export interface BuildCursorBasedPaginationResult<Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>> {
|
|
154
|
+
decodeCursorResult: DecodeCursorResult<InferModelFromPgTableWithColumns<Table>, Key>
|
|
155
|
+
whereSql: d.SQL | undefined
|
|
156
|
+
orderBySql: d.SQL[]
|
|
157
|
+
getNextCursor: (list: Array<InferModelFromPgTableWithColumns<Table>>) => Cursor
|
|
158
|
+
}
|
|
159
|
+
export const buildCursorBasedPagination = <Table extends AnyPgTableWithColumns, Key extends keyof InferModelFromPgTableWithColumns<Table>>(
|
|
160
|
+
options: BuildCursorBasedPaginationOptions<Table, Key>,
|
|
161
|
+
): BuildCursorBasedPaginationResult<Table, Key> => {
|
|
162
|
+
const { table, orderBy, limit, cursor } = options
|
|
163
|
+
|
|
164
|
+
type Model = InferModelFromPgTableWithColumns<Table>
|
|
165
|
+
|
|
166
|
+
const decodeCursorResult = decodeCursor({
|
|
167
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
168
|
+
modelForType: {} as Model,
|
|
169
|
+
cursor,
|
|
170
|
+
orderBy,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const whereSql = buildWhereSql({
|
|
174
|
+
table,
|
|
175
|
+
decodeCursorResult,
|
|
176
|
+
orderBy,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const orderBySql = buildOrderBySql({
|
|
180
|
+
table,
|
|
181
|
+
orderBy,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
const _getNextCursor = (list: Model[]): Cursor => {
|
|
185
|
+
return getNextCursor({
|
|
186
|
+
list,
|
|
187
|
+
limit,
|
|
188
|
+
encodeOptions: {
|
|
189
|
+
orderBy,
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
decodeCursorResult,
|
|
196
|
+
whereSql,
|
|
197
|
+
orderBySql,
|
|
198
|
+
getNextCursor: _getNextCursor,
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Email
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./resend.ts"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Resend } from "resend"
|
|
2
|
+
|
|
3
|
+
export interface SendEmailOptions {
|
|
4
|
+
from: string
|
|
5
|
+
to: string
|
|
6
|
+
subject: string
|
|
7
|
+
html: string
|
|
8
|
+
}
|
|
9
|
+
export type SendEmailResult = ReturnType<typeof Resend.prototype.emails.send>
|
|
10
|
+
|
|
11
|
+
export interface EmailOptions {
|
|
12
|
+
key: string
|
|
13
|
+
}
|
|
14
|
+
export class Email {
|
|
15
|
+
private sendClient: Resend
|
|
16
|
+
|
|
17
|
+
constructor(options: EmailOptions) {
|
|
18
|
+
const { key } = options
|
|
19
|
+
this.sendClient = new Resend(key)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {
|
|
23
|
+
return await this.sendClient.emails.send({ ...options })
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { InternalProxySubscriberEntryMap } from "./internal.ts"
|
|
2
2
|
import type {
|
|
3
|
-
|
|
3
|
+
EventsConstraint,
|
|
4
4
|
SubscriberEntry,
|
|
5
5
|
} from "./common.ts"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 表示 ClassEventProxy 用于接入目标实例及其事件系统的适配器。
|
|
9
9
|
*/
|
|
10
|
-
export interface ClassEventProxyTargetAdapter<Target, Events extends
|
|
10
|
+
export interface ClassEventProxyTargetAdapter<Target, Events extends EventsConstraint<Events>> {
|
|
11
11
|
emit<K extends keyof Events>(target: Target, event: K, ...args: Parameters<Events[K]>): boolean
|
|
12
12
|
subscribe<K extends keyof Events>(target: Target, event: K, subscriber: Events[K]): void
|
|
13
13
|
unsubscribe<K extends keyof Events>(target: Target, event: K, subscriber: Events[K]): void
|
|
@@ -16,7 +16,7 @@ export interface ClassEventProxyTargetAdapter<Target, Events extends BaseEvents>
|
|
|
16
16
|
/**
|
|
17
17
|
* 表示 ClassEventProxy 的构造选项。
|
|
18
18
|
*/
|
|
19
|
-
export interface ClassEventProxyOptions<Target, Events extends
|
|
19
|
+
export interface ClassEventProxyOptions<Target, Events extends EventsConstraint<Events>> {
|
|
20
20
|
targetAdapter: ClassEventProxyTargetAdapter<Target, Events>
|
|
21
21
|
/**
|
|
22
22
|
* 在代理管理的订阅者执行出错时接收错误与对应订阅项。
|
|
@@ -27,7 +27,7 @@ export interface ClassEventProxyOptions<Target, Events extends BaseEvents> {
|
|
|
27
27
|
/**
|
|
28
28
|
* 将一组目标实例的事件系统适配为统一的代理订阅模型。
|
|
29
29
|
*/
|
|
30
|
-
export class ClassEventProxy<Target, Events extends
|
|
30
|
+
export class ClassEventProxy<Target, Events extends EventsConstraint<Events>> {
|
|
31
31
|
protected options: ClassEventProxyOptions<Target, Events>
|
|
32
32
|
protected subscribers: Map<Target, InternalProxySubscriberEntryMap<Events>>
|
|
33
33
|
|
|
@@ -196,7 +196,8 @@ export class ClassEventProxy<Target, Events extends BaseEvents> {
|
|
|
196
196
|
|
|
197
197
|
const events = Object.keys(proxySubscriberEntryMap)
|
|
198
198
|
for (const event of events) {
|
|
199
|
-
|
|
199
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
200
|
+
this.removeSubscribersOfEvent(target, event as keyof Events)
|
|
200
201
|
}
|
|
201
202
|
this.subscribers.delete(target)
|
|
202
203
|
|
package/src/event/common.ts
CHANGED
|
@@ -4,14 +4,24 @@
|
|
|
4
4
|
// oxlint-disable-next-line no-explicit-any
|
|
5
5
|
export type BaseSubscriber = (...args: any[]) => void
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* 表示事件表中允许的事件-订阅者关系的约束类型。
|
|
8
|
+
* 该类型要求事件表的每个事件都必须对应一个 BaseSubscriber 类型的订阅者函数。
|
|
9
|
+
* 通过该约束类型,EventManager 和相关组件能够在编译时确保事件表的正确性。
|
|
8
10
|
*/
|
|
9
|
-
export type
|
|
11
|
+
export type EventsConstraint<Events extends EventsConstraint<Events>> = {
|
|
12
|
+
[K in keyof Events]: BaseSubscriber
|
|
13
|
+
}
|
|
14
|
+
export type BuildEvents<Events extends EventsConstraint<Events>> = {
|
|
15
|
+
[K in keyof Events]: Events[K]
|
|
16
|
+
}
|
|
17
|
+
export interface AnyEvents {
|
|
18
|
+
[event: string]: BaseSubscriber
|
|
19
|
+
}
|
|
10
20
|
|
|
11
21
|
/**
|
|
12
22
|
* 表示一次订阅关系的标准化描述。
|
|
13
23
|
*/
|
|
14
|
-
export interface SubscriberEntry<Events extends
|
|
24
|
+
export interface SubscriberEntry<Events extends EventsConstraint<Events>, K extends keyof Events> {
|
|
15
25
|
event: K
|
|
16
26
|
once: boolean
|
|
17
27
|
subscriber: Events[K]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { InternalSubscriberEntryMap } from "./internal.ts"
|
|
2
|
-
import type {
|
|
2
|
+
import type { EventsConstraint, SubscriberEntry } from "./common.ts"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 表示 EventManager 的构造选项。
|
|
6
6
|
*/
|
|
7
|
-
export interface EventManagerOptions<Events extends
|
|
7
|
+
export interface EventManagerOptions<Events extends EventsConstraint<Events>> {
|
|
8
8
|
/**
|
|
9
9
|
* 在订阅者执行出错时接收错误与对应订阅项。
|
|
10
10
|
*/
|
|
@@ -14,7 +14,7 @@ export interface EventManagerOptions<Events extends BaseEvents> {
|
|
|
14
14
|
/**
|
|
15
15
|
* 管理同一事件表下的订阅、取消订阅与事件派发。
|
|
16
16
|
*/
|
|
17
|
-
export class EventManager<Events extends
|
|
17
|
+
export class EventManager<Events extends EventsConstraint<Events>> {
|
|
18
18
|
protected options: EventManagerOptions<Events>
|
|
19
19
|
/**
|
|
20
20
|
* 每一类事件对应一个内部订阅映射,映射中的 key 是订阅者函数,value 是 SubscriberEntry 对象。
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { InternalProxySubscriberEntryMap } from "./internal.ts"
|
|
2
2
|
import type {
|
|
3
|
-
|
|
3
|
+
EventsConstraint,
|
|
4
4
|
SubscriberEntry,
|
|
5
5
|
} from "./common.ts"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 表示 InstanceEventProxy 用于接入底层事件目标的适配器。
|
|
9
9
|
*/
|
|
10
|
-
export interface InstanceEventProxyTargetAdapter<Events extends
|
|
10
|
+
export interface InstanceEventProxyTargetAdapter<Events extends EventsConstraint<Events>> {
|
|
11
11
|
emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): boolean
|
|
12
12
|
subscribe<K extends keyof Events>(event: K, subscriber: Events[K]): void
|
|
13
13
|
unsubscribe<K extends keyof Events>(event: K, subscriber: Events[K]): void
|
|
@@ -16,7 +16,7 @@ export interface InstanceEventProxyTargetAdapter<Events extends BaseEvents> {
|
|
|
16
16
|
/**
|
|
17
17
|
* 表示 InstanceEventProxy 的构造选项。
|
|
18
18
|
*/
|
|
19
|
-
export interface InstanceEventProxyOptions<Events extends
|
|
19
|
+
export interface InstanceEventProxyOptions<Events extends EventsConstraint<Events>> {
|
|
20
20
|
targetAdapter: InstanceEventProxyTargetAdapter<Events>
|
|
21
21
|
/**
|
|
22
22
|
* 在代理管理的订阅者执行出错时接收错误与对应订阅项。
|
|
@@ -27,7 +27,7 @@ export interface InstanceEventProxyOptions<Events extends BaseEvents> {
|
|
|
27
27
|
/**
|
|
28
28
|
* 将单个事件目标适配为可管理下游订阅关系的代理。
|
|
29
29
|
*/
|
|
30
|
-
export class InstanceEventProxy<Events extends
|
|
30
|
+
export class InstanceEventProxy<Events extends EventsConstraint<Events>> {
|
|
31
31
|
protected options: InstanceEventProxyOptions<Events>
|
|
32
32
|
protected subscribers: InternalProxySubscriberEntryMap<Events>
|
|
33
33
|
|
|
@@ -168,7 +168,8 @@ export class InstanceEventProxy<Events extends BaseEvents> {
|
|
|
168
168
|
const events = Object.keys(this.subscribers)
|
|
169
169
|
|
|
170
170
|
for (const event of events) {
|
|
171
|
-
|
|
171
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
172
|
+
this.removeSubscribersOfEvent(event as keyof Events)
|
|
172
173
|
}
|
|
173
174
|
this.subscribers = {}
|
|
174
175
|
|
package/src/event/internal.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EventsConstraint, SubscriberEntry } from "./common.ts"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 表示某一事件名下由订阅者函数到订阅项的内部映射。
|
|
5
5
|
*/
|
|
6
|
-
export type InternalSubscriberEntryMap<Events extends
|
|
6
|
+
export type InternalSubscriberEntryMap<Events extends EventsConstraint<Events>, K extends keyof Events> = Map<
|
|
7
7
|
Events[K],
|
|
8
8
|
SubscriberEntry<Events, K>
|
|
9
9
|
>
|
|
@@ -11,7 +11,7 @@ export type InternalSubscriberEntryMap<Events extends BaseEvents, K extends keyo
|
|
|
11
11
|
/**
|
|
12
12
|
* 表示代理订阅项及其所管理的下游订阅集合的内部结构。
|
|
13
13
|
*/
|
|
14
|
-
export interface InternalProxySubscriberEntry<Events extends
|
|
14
|
+
export interface InternalProxySubscriberEntry<Events extends EventsConstraint<Events>, K extends keyof Events> {
|
|
15
15
|
managedSubscribers: InternalSubscriberEntryMap<Events, K>
|
|
16
16
|
proxySubscriber: Events[K]
|
|
17
17
|
}
|
|
@@ -19,6 +19,6 @@ export interface InternalProxySubscriberEntry<Events extends BaseEvents, K exten
|
|
|
19
19
|
/**
|
|
20
20
|
* 使用对象而非 Map 来保存事件到代理订阅项的内部映射。
|
|
21
21
|
*/
|
|
22
|
-
export type InternalProxySubscriberEntryMap<Events extends
|
|
22
|
+
export type InternalProxySubscriberEntryMap<Events extends EventsConstraint<Events>> = {
|
|
23
23
|
[K in keyof Events]?: InternalProxySubscriberEntry<Events, K> | undefined
|
|
24
24
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Form
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Form 模块提供围绕类表单输入项(form-like input)及其组合关系的通用建模能力,用于表达输入状态、值转换与聚合边界。
|
|
6
|
+
|
|
7
|
+
它关注的不是某个组件库的渲染细节,也不是某个框架各自的表单接入方式,而是“一个输入项应如何被视为可独立管理的输入原子、多个输入项如何组合成更高层的类表单数据管理器、以及输入值与聚合值如何保持清楚且可维护的对应关系”这一类基础问题。它之所以被命名为 Form,不是因为它只服务于页面表单,而是因为表单场景最自然、最直观地体现了这类模型的价值。
|
|
8
|
+
|
|
9
|
+
## For Understanding
|
|
10
|
+
|
|
11
|
+
理解 Form 模块时,首先应把它看成“类表单数据管理模型”,而不是“页面表单组件集合”或“校验规则工具箱”。它要解决的问题,不是把某个现成表单 API 重新包装一遍,而是为输入项本身建立稳定语义:一个输入项有哪些核心状态,输入过程与最终值如何区分,多个输入项如何组合,以及组合之后的整体值应如何被表达。
|
|
12
|
+
|
|
13
|
+
这个模块更适合放在以下边界中:
|
|
14
|
+
|
|
15
|
+
- 你希望把输入逻辑从具体接入层中抽离出来,让输入状态、值更新和聚合关系可以在不同 UI、服务端流程或其它非界面场景之间复用。
|
|
16
|
+
- 你需要先把输入项视为一组可独立管理的输入原子,再把它们组合成更高层的类表单数据管理器,而不是一开始就把所有逻辑揉成单个大对象。
|
|
17
|
+
- 你需要明确区分“输入过程中的值”和“对外稳定表达的值”,并希望这种区分能够成为长期可维护的公共语义。
|
|
18
|
+
|
|
19
|
+
理解这个模块时,还应守住几条边界原则:
|
|
20
|
+
|
|
21
|
+
- Form 模块表达的是输入项状态、输入项组合与值映射语义,不负责页面布局、样式、校验文案或具体提交流程。
|
|
22
|
+
- 它可以服务于具体输入组件,但不应因为适配某个框架或某种表单交互习惯,而把这些宿主细节直接上升为模块公共语义。
|
|
23
|
+
- 它关注的是“输入项如何被建模、如何更新、如何聚合”,而不是所有表单相关问题的总容器;像复杂校验编排、错误展示策略、网络提交流程或业务字段规范,通常应由更上层模块承担。
|
|
24
|
+
|
|
25
|
+
因此,更合适的理解方式是:Form 模块提供的是一套围绕输入原子与类表单聚合的数据管理基础模型能力。表单只是它最典型的落点之一,而不是它的唯一归宿。只有当某个能力确实在澄清这条边界时,它才适合作为该模块的组成部分;如果某个能力只是某类页面表单的临时便利封装,那么它通常不应直接进入这个模块。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./inputor-controller/index.ts"
|