@downcity/city 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +182 -0
  2. package/bin/core/auth/authenticator.d.ts +79 -0
  3. package/bin/core/auth/authenticator.d.ts.map +1 -0
  4. package/bin/core/auth/authenticator.js +125 -0
  5. package/bin/core/auth/authenticator.js.map +1 -0
  6. package/bin/core/auth/token-signer.d.ts +27 -0
  7. package/bin/core/auth/token-signer.d.ts.map +1 -0
  8. package/bin/core/auth/token-signer.js +116 -0
  9. package/bin/core/auth/token-signer.js.map +1 -0
  10. package/bin/core/auth/types.d.ts +94 -0
  11. package/bin/core/auth/types.d.ts.map +1 -0
  12. package/bin/core/auth/types.js +7 -0
  13. package/bin/core/auth/types.js.map +1 -0
  14. package/bin/core/base/base-env-catalog.d.ts +13 -0
  15. package/bin/core/base/base-env-catalog.d.ts.map +1 -0
  16. package/bin/core/base/base-env-catalog.js +85 -0
  17. package/bin/core/base/base-env-catalog.js.map +1 -0
  18. package/bin/core/base/base-init.d.ts +48 -0
  19. package/bin/core/base/base-init.d.ts.map +1 -0
  20. package/bin/core/base/base-init.js +105 -0
  21. package/bin/core/base/base-init.js.map +1 -0
  22. package/bin/core/base/base-instruction.d.ts +11 -0
  23. package/bin/core/base/base-instruction.d.ts.map +1 -0
  24. package/bin/core/base/base-instruction.js +37 -0
  25. package/bin/core/base/base-instruction.js.map +1 -0
  26. package/bin/core/base/base-router.d.ts +41 -0
  27. package/bin/core/base/base-router.d.ts.map +1 -0
  28. package/bin/core/base/base-router.js +222 -0
  29. package/bin/core/base/base-router.js.map +1 -0
  30. package/bin/core/base/base-runtime.d.ts +13 -0
  31. package/bin/core/base/base-runtime.d.ts.map +1 -0
  32. package/bin/core/base/base-runtime.js +116 -0
  33. package/bin/core/base/base-runtime.js.map +1 -0
  34. package/bin/core/base/base.d.ts +82 -0
  35. package/bin/core/base/base.d.ts.map +1 -0
  36. package/bin/core/base/base.js +156 -0
  37. package/bin/core/base/base.js.map +1 -0
  38. package/bin/core/runtime.d.ts +120 -0
  39. package/bin/core/runtime.d.ts.map +1 -0
  40. package/bin/core/runtime.js +13 -0
  41. package/bin/core/runtime.js.map +1 -0
  42. package/bin/core/types.d.ts +60 -0
  43. package/bin/core/types.d.ts.map +1 -0
  44. package/bin/core/types.js +7 -0
  45. package/bin/core/types.js.map +1 -0
  46. package/bin/index.d.ts +33 -0
  47. package/bin/index.d.ts.map +1 -0
  48. package/bin/index.js +37 -0
  49. package/bin/index.js.map +1 -0
  50. package/bin/service/action.d.ts +44 -0
  51. package/bin/service/action.d.ts.map +1 -0
  52. package/bin/service/action.js +48 -0
  53. package/bin/service/action.js.map +1 -0
  54. package/bin/service/ai/ai-service.d.ts +56 -0
  55. package/bin/service/ai/ai-service.d.ts.map +1 -0
  56. package/bin/service/ai/ai-service.js +262 -0
  57. package/bin/service/ai/ai-service.js.map +1 -0
  58. package/bin/service/ai/openai-compatible-provider.d.ts +54 -0
  59. package/bin/service/ai/openai-compatible-provider.d.ts.map +1 -0
  60. package/bin/service/ai/openai-compatible-provider.js +175 -0
  61. package/bin/service/ai/openai-compatible-provider.js.map +1 -0
  62. package/bin/service/ai/provider.d.ts +43 -0
  63. package/bin/service/ai/provider.d.ts.map +1 -0
  64. package/bin/service/ai/provider.js +50 -0
  65. package/bin/service/ai/provider.js.map +1 -0
  66. package/bin/service/ai/types.d.ts +94 -0
  67. package/bin/service/ai/types.d.ts.map +1 -0
  68. package/bin/service/ai/types.js +7 -0
  69. package/bin/service/ai/types.js.map +1 -0
  70. package/bin/service/env/env-service.d.ts +12 -0
  71. package/bin/service/env/env-service.d.ts.map +1 -0
  72. package/bin/service/env/env-service.js +39 -0
  73. package/bin/service/env/env-service.js.map +1 -0
  74. package/bin/service/env/env-store.d.ts +15 -0
  75. package/bin/service/env/env-store.d.ts.map +1 -0
  76. package/bin/service/env/env-store.js +35 -0
  77. package/bin/service/env/env-store.js.map +1 -0
  78. package/bin/service/env/schema.d.ts +171 -0
  79. package/bin/service/env/schema.d.ts.map +1 -0
  80. package/bin/service/env/schema.js +52 -0
  81. package/bin/service/env/schema.js.map +1 -0
  82. package/bin/service/env/types.d.ts +79 -0
  83. package/bin/service/env/types.d.ts.map +1 -0
  84. package/bin/service/env/types.js +8 -0
  85. package/bin/service/env/types.js.map +1 -0
  86. package/bin/service/hook.d.ts +23 -0
  87. package/bin/service/hook.d.ts.map +1 -0
  88. package/bin/service/hook.js +49 -0
  89. package/bin/service/hook.js.map +1 -0
  90. package/bin/service/installable-service.d.ts +75 -0
  91. package/bin/service/installable-service.d.ts.map +1 -0
  92. package/bin/service/installable-service.js +89 -0
  93. package/bin/service/installable-service.js.map +1 -0
  94. package/bin/service/instruction.d.ts +106 -0
  95. package/bin/service/instruction.d.ts.map +1 -0
  96. package/bin/service/instruction.js +70 -0
  97. package/bin/service/instruction.js.map +1 -0
  98. package/bin/service/service.d.ts +140 -0
  99. package/bin/service/service.d.ts.map +1 -0
  100. package/bin/service/service.js +94 -0
  101. package/bin/service/service.js.map +1 -0
  102. package/bin/service/studios/schema.d.ts +207 -0
  103. package/bin/service/studios/schema.d.ts.map +1 -0
  104. package/bin/service/studios/schema.js +60 -0
  105. package/bin/service/studios/schema.js.map +1 -0
  106. package/bin/service/studios/studio-store.d.ts +17 -0
  107. package/bin/service/studios/studio-store.d.ts.map +1 -0
  108. package/bin/service/studios/studio-store.js +49 -0
  109. package/bin/service/studios/studio-store.js.map +1 -0
  110. package/bin/service/studios/studios-service.d.ts +15 -0
  111. package/bin/service/studios/studios-service.d.ts.map +1 -0
  112. package/bin/service/studios/studios-service.js +51 -0
  113. package/bin/service/studios/studios-service.js.map +1 -0
  114. package/bin/service/studios/types.d.ts +51 -0
  115. package/bin/service/studios/types.d.ts.map +1 -0
  116. package/bin/service/studios/types.js +7 -0
  117. package/bin/service/studios/types.js.map +1 -0
  118. package/bin/service/types.d.ts +11 -0
  119. package/bin/service/types.d.ts.map +1 -0
  120. package/bin/service/types.js +5 -0
  121. package/bin/service/types.js.map +1 -0
  122. package/bin/store/db.d.ts +61 -0
  123. package/bin/store/db.d.ts.map +1 -0
  124. package/bin/store/db.js +29 -0
  125. package/bin/store/db.js.map +1 -0
  126. package/bin/store/table-api.d.ts +42 -0
  127. package/bin/store/table-api.d.ts.map +1 -0
  128. package/bin/store/table-api.js +100 -0
  129. package/bin/store/table-api.js.map +1 -0
  130. package/bin/store/types.d.ts +14 -0
  131. package/bin/store/types.d.ts.map +1 -0
  132. package/bin/store/types.js +7 -0
  133. package/bin/store/types.js.map +1 -0
  134. package/bin/utils/helpers.d.ts +101 -0
  135. package/bin/utils/helpers.d.ts.map +1 -0
  136. package/bin/utils/helpers.js +192 -0
  137. package/bin/utils/helpers.js.map +1 -0
  138. package/bin/utils/validation.d.ts +18 -0
  139. package/bin/utils/validation.d.ts.map +1 -0
  140. package/bin/utils/validation.js +28 -0
  141. package/bin/utils/validation.js.map +1 -0
  142. package/package.json +55 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Env 数据库 schema 模块。
3
+ *
4
+ * 定义 Downcity 内置的 Env 表结构(SQLite + Postgres)。
5
+ * Env 表存储运行时环境变量,优先级高于 `.env` 文件。
6
+ */
7
+ /**
8
+ * 默认 SQLite Env 表。
9
+ */
10
+ export declare const sqliteEnv: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
11
+ name: "env";
12
+ schema: undefined;
13
+ columns: {
14
+ key: import("drizzle-orm/sqlite-core").SQLiteColumn<{
15
+ name: "key";
16
+ tableName: "env";
17
+ dataType: "string";
18
+ columnType: "SQLiteText";
19
+ data: string;
20
+ driverParam: string;
21
+ notNull: true;
22
+ hasDefault: false;
23
+ isPrimaryKey: true;
24
+ isAutoincrement: false;
25
+ hasRuntimeDefault: false;
26
+ enumValues: [string, ...string[]];
27
+ baseColumn: never;
28
+ identity: undefined;
29
+ generated: undefined;
30
+ }, {}, {
31
+ length: number | undefined;
32
+ }>;
33
+ value: import("drizzle-orm/sqlite-core").SQLiteColumn<{
34
+ name: "value";
35
+ tableName: "env";
36
+ dataType: "string";
37
+ columnType: "SQLiteText";
38
+ data: string;
39
+ driverParam: string;
40
+ notNull: true;
41
+ hasDefault: false;
42
+ isPrimaryKey: false;
43
+ isAutoincrement: false;
44
+ hasRuntimeDefault: false;
45
+ enumValues: [string, ...string[]];
46
+ baseColumn: never;
47
+ identity: undefined;
48
+ generated: undefined;
49
+ }, {}, {
50
+ length: number | undefined;
51
+ }>;
52
+ created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
53
+ name: "created_at";
54
+ tableName: "env";
55
+ dataType: "string";
56
+ columnType: "SQLiteText";
57
+ data: string;
58
+ driverParam: string;
59
+ notNull: true;
60
+ hasDefault: false;
61
+ isPrimaryKey: false;
62
+ isAutoincrement: false;
63
+ hasRuntimeDefault: false;
64
+ enumValues: [string, ...string[]];
65
+ baseColumn: never;
66
+ identity: undefined;
67
+ generated: undefined;
68
+ }, {}, {
69
+ length: number | undefined;
70
+ }>;
71
+ updated_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
72
+ name: "updated_at";
73
+ tableName: "env";
74
+ dataType: "string";
75
+ columnType: "SQLiteText";
76
+ data: string;
77
+ driverParam: string;
78
+ notNull: true;
79
+ hasDefault: false;
80
+ isPrimaryKey: false;
81
+ isAutoincrement: false;
82
+ hasRuntimeDefault: false;
83
+ enumValues: [string, ...string[]];
84
+ baseColumn: never;
85
+ identity: undefined;
86
+ generated: undefined;
87
+ }, {}, {
88
+ length: number | undefined;
89
+ }>;
90
+ };
91
+ dialect: "sqlite";
92
+ }>;
93
+ /**
94
+ * 默认 Postgres Env 表。
95
+ */
96
+ export declare const pgEnv: import("drizzle-orm/pg-core").PgTableWithColumns<{
97
+ name: "env";
98
+ schema: undefined;
99
+ columns: {
100
+ key: import("drizzle-orm/pg-core").PgColumn<{
101
+ name: "key";
102
+ tableName: "env";
103
+ dataType: "string";
104
+ columnType: "PgText";
105
+ data: string;
106
+ driverParam: string;
107
+ notNull: true;
108
+ hasDefault: false;
109
+ isPrimaryKey: true;
110
+ isAutoincrement: false;
111
+ hasRuntimeDefault: false;
112
+ enumValues: [string, ...string[]];
113
+ baseColumn: never;
114
+ identity: undefined;
115
+ generated: undefined;
116
+ }, {}, {}>;
117
+ value: import("drizzle-orm/pg-core").PgColumn<{
118
+ name: "value";
119
+ tableName: "env";
120
+ dataType: "string";
121
+ columnType: "PgText";
122
+ data: string;
123
+ driverParam: string;
124
+ notNull: true;
125
+ hasDefault: false;
126
+ isPrimaryKey: false;
127
+ isAutoincrement: false;
128
+ hasRuntimeDefault: false;
129
+ enumValues: [string, ...string[]];
130
+ baseColumn: never;
131
+ identity: undefined;
132
+ generated: undefined;
133
+ }, {}, {}>;
134
+ created_at: import("drizzle-orm/pg-core").PgColumn<{
135
+ name: "created_at";
136
+ tableName: "env";
137
+ dataType: "string";
138
+ columnType: "PgText";
139
+ data: string;
140
+ driverParam: string;
141
+ notNull: true;
142
+ hasDefault: false;
143
+ isPrimaryKey: false;
144
+ isAutoincrement: false;
145
+ hasRuntimeDefault: false;
146
+ enumValues: [string, ...string[]];
147
+ baseColumn: never;
148
+ identity: undefined;
149
+ generated: undefined;
150
+ }, {}, {}>;
151
+ updated_at: import("drizzle-orm/pg-core").PgColumn<{
152
+ name: "updated_at";
153
+ tableName: "env";
154
+ dataType: "string";
155
+ columnType: "PgText";
156
+ data: string;
157
+ driverParam: string;
158
+ notNull: true;
159
+ hasDefault: false;
160
+ isPrimaryKey: false;
161
+ isAutoincrement: false;
162
+ hasRuntimeDefault: false;
163
+ enumValues: [string, ...string[]];
164
+ baseColumn: never;
165
+ identity: undefined;
166
+ generated: undefined;
167
+ }, {}, {}>;
168
+ };
169
+ dialect: "pg";
170
+ }>;
171
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/service/env/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBhB,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Env 数据库 schema 模块。
3
+ *
4
+ * 定义 Downcity 内置的 Env 表结构(SQLite + Postgres)。
5
+ * Env 表存储运行时环境变量,优先级高于 `.env` 文件。
6
+ */
7
+ import { pgTable, text as pgText } from "drizzle-orm/pg-core";
8
+ import { sqliteTable, text as sqliteText } from "drizzle-orm/sqlite-core";
9
+ const DEFAULT_ENV_TABLE = "env";
10
+ /**
11
+ * 默认 SQLite Env 表。
12
+ */
13
+ export const sqliteEnv = sqliteTable(DEFAULT_ENV_TABLE, {
14
+ /**
15
+ * Env key。
16
+ */
17
+ key: sqliteText("key").primaryKey(),
18
+ /**
19
+ * Env value。
20
+ */
21
+ value: sqliteText("value").notNull(),
22
+ /**
23
+ * 创建时间。
24
+ */
25
+ created_at: sqliteText("created_at").notNull(),
26
+ /**
27
+ * 更新时间。
28
+ */
29
+ updated_at: sqliteText("updated_at").notNull(),
30
+ });
31
+ /**
32
+ * 默认 Postgres Env 表。
33
+ */
34
+ export const pgEnv = pgTable(DEFAULT_ENV_TABLE, {
35
+ /**
36
+ * Env key。
37
+ */
38
+ key: pgText("key").primaryKey(),
39
+ /**
40
+ * Env value。
41
+ */
42
+ value: pgText("value").notNull(),
43
+ /**
44
+ * 创建时间。
45
+ */
46
+ created_at: pgText("created_at").notNull(),
47
+ /**
48
+ * 更新时间。
49
+ */
50
+ updated_at: pgText("updated_at").notNull(),
51
+ });
52
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/service/env/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1E,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC,iBAAiB,EAAE;IACtD;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE;IAEnC;;OAEG;IACH,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAEpC;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAE9C;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC/C,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,EAAE;IAC9C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE;IAE/B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAEhC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAE1C;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC3C,CAAC,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Env 层公共类型。
3
+ *
4
+ * Downcity 的业务 env 统一由 City 内部管理。
5
+ * Service / Model 只声明自己需要哪些 key,具体值统一写入 City 的 env 表。
6
+ */
7
+ /**
8
+ * Runtime env 列表中的单条记录。
9
+ */
10
+ export interface EnvEntry extends Record<string, unknown> {
11
+ /**
12
+ * 标准化后的环境变量名。
13
+ */
14
+ key: string;
15
+ /**
16
+ * 环境变量明文值。
17
+ */
18
+ value: string;
19
+ /**
20
+ * env 值来源。
21
+ */
22
+ source: "database";
23
+ }
24
+ /**
25
+ * 写入 env 时的输入。
26
+ */
27
+ export interface EnvUpsertInput {
28
+ /**
29
+ * 环境变量名。
30
+ */
31
+ key: string;
32
+ /**
33
+ * 环境变量值。
34
+ */
35
+ value: string;
36
+ }
37
+ /**
38
+ * 单个 env requirement 的当前状态。
39
+ */
40
+ export interface EnvRequirementStatus {
41
+ /**
42
+ * 环境变量 key。
43
+ */
44
+ key: string;
45
+ /**
46
+ * 给管理员展示的说明文本。
47
+ */
48
+ description: string;
49
+ /**
50
+ * 当前是否必填。
51
+ */
52
+ required: boolean;
53
+ /**
54
+ * 当前 City 是否已经配置了该 key。
55
+ */
56
+ configured: boolean;
57
+ /**
58
+ * 当前值的安全预览文本。
59
+ */
60
+ value_preview?: string;
61
+ }
62
+ /**
63
+ * City 聚合后的 env 配置分组。
64
+ */
65
+ export interface EnvCatalogScope {
66
+ /**
67
+ * 分组唯一标识。
68
+ */
69
+ id: string;
70
+ /**
71
+ * 分组展示名称。
72
+ */
73
+ name: string;
74
+ /**
75
+ * 当前分组下的 env requirement 列表。
76
+ */
77
+ env: EnvRequirementStatus[];
78
+ }
79
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/service/env/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,QAAS,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvD;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,oBAAoB,EAAE,CAAC;CAC7B"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Env 层公共类型。
3
+ *
4
+ * Downcity 的业务 env 统一由 City 内部管理。
5
+ * Service / Model 只声明自己需要哪些 key,具体值统一写入 City 的 env 表。
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/service/env/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Hook 模块。
3
+ *
4
+ * Hook 是执行生命周期中可注册的回调链,支持 before / after / onError 三个阶段。
5
+ * 所有 hook 函数接收 Context,不返回值(直接在 ctx 上修改)。
6
+ */
7
+ import type { Context } from "./service.js";
8
+ /** Hook 回调函数 */
9
+ export type HookFn = (ctx: Context) => void | Promise<void>;
10
+ export declare class Hook {
11
+ private befores;
12
+ private afters;
13
+ private errors;
14
+ before(fn: HookFn): this;
15
+ after(fn: HookFn): this;
16
+ onError(fn: HookFn): this;
17
+ runBefore(ctx: Context): Promise<void>;
18
+ runAfter(ctx: Context): Promise<void>;
19
+ runOnError(ctx: Context): Promise<void>;
20
+ /** 清空所有 hook */
21
+ clear(): this;
22
+ }
23
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/service/hook.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,gBAAgB;AAChB,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5D,qBAAa,IAAI;IACf,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAgB;IAE9B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKxB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKvB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKnB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrC,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,gBAAgB;IAChB,KAAK,IAAI,IAAI;CAMd"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Hook 模块。
3
+ *
4
+ * Hook 是执行生命周期中可注册的回调链,支持 before / after / onError 三个阶段。
5
+ * 所有 hook 函数接收 Context,不返回值(直接在 ctx 上修改)。
6
+ */
7
+ export class Hook {
8
+ befores = [];
9
+ afters = [];
10
+ errors = [];
11
+ before(fn) {
12
+ this.befores.push(fn);
13
+ return this;
14
+ }
15
+ after(fn) {
16
+ this.afters.push(fn);
17
+ return this;
18
+ }
19
+ onError(fn) {
20
+ this.errors.push(fn);
21
+ return this;
22
+ }
23
+ async runBefore(ctx) {
24
+ for (const fn of this.befores) {
25
+ await fn(ctx);
26
+ }
27
+ }
28
+ async runAfter(ctx) {
29
+ for (const fn of this.afters) {
30
+ await fn(ctx);
31
+ }
32
+ }
33
+ async runOnError(ctx) {
34
+ for (const fn of this.errors) {
35
+ try {
36
+ await fn(ctx);
37
+ }
38
+ catch { /* 不覆盖原始错误 */ }
39
+ }
40
+ }
41
+ /** 清空所有 hook */
42
+ clear() {
43
+ this.befores = [];
44
+ this.afters = [];
45
+ this.errors = [];
46
+ return this;
47
+ }
48
+ }
49
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/service/hook.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,OAAO,IAAI;IACP,OAAO,GAAa,EAAE,CAAC;IACvB,MAAM,GAAa,EAAE,CAAC;IACtB,MAAM,GAAa,EAAE,CAAC;IAE9B,MAAM,CAAC,EAAU;QACf,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,EAAU;QACd,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAY;QAC1B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAY;QACzB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAY;QAC3B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC;gBAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 可安装服务桥接模块。
3
+ *
4
+ * 提供 `InstallableService` 基类,把带 `install(ctx)` 生命周期的服务
5
+ * 统一桥接到 `Service.action()` 体系。
6
+ */
7
+ import { Service, type Context, type EnvRequirement } from "./service.js";
8
+ import { Hook } from "./hook.js";
9
+ import type { CityTableApi } from "../store/table-api.js";
10
+ import type { CreateUserTokenInput, UserTokenIssueResult, RuntimeUser } from "../core/auth/types.js";
11
+ import type { InstructionDefinition } from "./instruction.js";
12
+ export interface ServiceInstallContext {
13
+ table<TRow extends Record<string, unknown> = Record<string, unknown>>(name: string): CityTableApi<TRow>;
14
+ route(config: {
15
+ method: "GET" | "POST";
16
+ path: string;
17
+ auth?: Array<"user" | "admin">;
18
+ handler: (ctx: ServiceRouteContext) => Promise<Response> | Response;
19
+ }): void;
20
+ hook: {
21
+ before(fn: (ctx: Context) => Promise<void> | void): void;
22
+ after(fn: (ctx: Context) => Promise<void> | void): void;
23
+ onError(fn: (ctx: Context) => Promise<void> | void): void;
24
+ };
25
+ createUserToken(input: CreateUserTokenInput): Promise<UserTokenIssueResult>;
26
+ env(key: string): string | undefined;
27
+ }
28
+ export interface ServiceRouteContext {
29
+ /** 当前 user_token 解析出的用户;免登录或 admin 请求时可能为空 */
30
+ user?: RuntimeUser;
31
+ /** 当前 user_token 对应的 studio;免登录或 admin 请求时可能为空 */
32
+ studio?: {
33
+ studio_id: string;
34
+ status: string;
35
+ };
36
+ /** 原始 HTTP 请求 */
37
+ request: Request;
38
+ /** 读取已解析 JSON 请求体 */
39
+ json<T extends Record<string, unknown> = Record<string, unknown>>(): Promise<T>;
40
+ /** 读取原始文本请求体 */
41
+ text(): Promise<string>;
42
+ /** 快速返回 JSON Response */
43
+ jsonResponse(body: unknown, status?: number): Response;
44
+ }
45
+ export declare abstract class InstallableService extends Service {
46
+ readonly schema?: Record<string, any>;
47
+ /**
48
+ * 服务级全局 hook。
49
+ *
50
+ * 通过 ServiceInstallContext.hook 注册,City 会在所有 Service Action
51
+ * 执行时统一触发。服务自己的路由仍然通过 Service.action() 暴露。
52
+ */
53
+ readonly globalHook: Hook;
54
+ constructor(env?: EnvRequirement[]);
55
+ abstract install(ctx: ServiceInstallContext): void;
56
+ _onInit(): Promise<void>;
57
+ }
58
+ export type ServiceDefinition = {
59
+ id: string;
60
+ name?: string;
61
+ version?: string;
62
+ schema?: Record<string, unknown>;
63
+ /**
64
+ * 服务依赖的运行时环境变量声明。
65
+ *
66
+ * 关键说明(中文)
67
+ * - 这些 key 会出现在 `/v1/services` 中,供 manager / admin UI 展示
68
+ * - City 会把这些 key 自动注册到自己的 env requirement 目录中
69
+ */
70
+ env?: EnvRequirement[];
71
+ instruction?: InstructionDefinition;
72
+ install(ctx: ServiceInstallContext): void;
73
+ };
74
+ export declare function asInstallableService(bp: ServiceDefinition): InstallableService;
75
+ //# sourceMappingURL=installable-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installable-service.d.ts","sourceRoot":"","sources":["../../src/service/installable-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * 可安装服务桥接模块。
3
+ *
4
+ * 提供 `InstallableService` 基类,把带 `install(ctx)` 生命周期的服务
5
+ * 统一桥接到 `Service.action()` 体系。
6
+ */
7
+ import { Service } from "./service.js";
8
+ import { Hook } from "./hook.js";
9
+ import { TableApi } from "../store/table-api.js";
10
+ // ===========================================================================
11
+ // InstallableService 基类
12
+ // ===========================================================================
13
+ export class InstallableService extends Service {
14
+ schema;
15
+ /**
16
+ * 服务级全局 hook。
17
+ *
18
+ * 通过 ServiceInstallContext.hook 注册,City 会在所有 Service Action
19
+ * 执行时统一触发。服务自己的路由仍然通过 Service.action() 暴露。
20
+ */
21
+ globalHook = new Hook();
22
+ constructor(env) {
23
+ super({ id: "service", env });
24
+ }
25
+ async _onInit() {
26
+ if (this.schema && !this.tables) {
27
+ this.tables = this.schema;
28
+ }
29
+ const self = this;
30
+ const ctx = {
31
+ table(name) {
32
+ if (!self._db)
33
+ throw new Error("InstallableService database is not ready");
34
+ const table = self.tables?.[name];
35
+ if (!table)
36
+ throw new Error(`Unknown table: ${name}`);
37
+ return new TableApi(self._db, table);
38
+ },
39
+ route(config) {
40
+ // 去掉前导 /,与 client ServiceClient.action() 的 normalizeName 对齐
41
+ const actionId = config.path.replace(/^\/+/, "");
42
+ self.action(actionId, async (svcCtx) => {
43
+ return await config.handler({
44
+ user: svcCtx.user,
45
+ studio: svcCtx.studio,
46
+ request: svcCtx.request ?? new Request("http://local"),
47
+ json: async () => svcCtx.input,
48
+ text: async () => svcCtx.raw_body ?? JSON.stringify(svcCtx.input),
49
+ jsonResponse: (body, status) => new Response(JSON.stringify(body), {
50
+ status, headers: { "content-type": "application/json" },
51
+ }),
52
+ });
53
+ }, { method: config.method, auth: config.auth });
54
+ },
55
+ hook: {
56
+ before(fn) { self.globalHook.before(fn); },
57
+ after(fn) { self.globalHook.after(fn); },
58
+ onError(fn) { self.globalHook.onError(fn); },
59
+ },
60
+ async createUserToken(input) {
61
+ if (!self._authenticator)
62
+ throw new Error("Authenticator not ready");
63
+ return self._authenticator.createToken(input);
64
+ },
65
+ env(key) {
66
+ return self._env?.get(key);
67
+ },
68
+ };
69
+ this.install(ctx);
70
+ }
71
+ }
72
+ export function asInstallableService(bp) {
73
+ return new (class extends InstallableService {
74
+ constructor() {
75
+ super(bp.env);
76
+ this.id = bp.id;
77
+ if (bp.name)
78
+ this.name = bp.name;
79
+ if (bp.schema)
80
+ this.tables = bp.schema;
81
+ if (bp.instruction)
82
+ this.instruction = bp.instruction;
83
+ }
84
+ install(ctx) {
85
+ bp.install(ctx);
86
+ }
87
+ })();
88
+ }
89
+ //# sourceMappingURL=installable-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installable-service.js","sourceRoot":"","sources":["../../src/service/installable-service.ts"],"names":[],"mappings":"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"}