@plitzi/sdk-server 0.33.2 → 0.34.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.
Files changed (274) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +283 -8
  3. package/dist/actions.d.ts +62 -0
  4. package/dist/actions.js +12 -0
  5. package/dist/adapters/cloudAdapters.d.ts +72 -0
  6. package/dist/adapters/cloudAdapters.js +303 -0
  7. package/dist/adapters/space/index.d.ts +93 -0
  8. package/dist/adapters/space/index.js +122 -0
  9. package/dist/adapters/space/resolvers.d.ts +69 -0
  10. package/dist/adapters/space/resolvers.js +79 -0
  11. package/dist/adapters/space/space.test.d.ts +1 -0
  12. package/dist/adapters/space/types.d.ts +64 -0
  13. package/dist/adapters/space/types.js +41 -0
  14. package/dist/auth.d.ts +50 -0
  15. package/dist/auth.js +19 -0
  16. package/dist/core/auth/api.d.ts +400 -11
  17. package/dist/core/auth/api.js +1044 -50
  18. package/dist/core/auth/createAuth.d.ts +135 -30
  19. package/dist/core/auth/createAuth.js +96 -24
  20. package/dist/core/auth/credentials.d.ts +9 -3
  21. package/dist/core/auth/credentials.js +10 -4
  22. package/dist/core/auth/csrf.d.ts +89 -0
  23. package/dist/core/auth/csrf.js +234 -0
  24. package/dist/core/auth/csrf.test.d.ts +1 -0
  25. package/dist/core/auth/identity.d.ts +6 -0
  26. package/dist/core/auth/identity.js +37 -8
  27. package/dist/core/auth/lifecycle.test.d.ts +1 -0
  28. package/dist/core/auth/oauth/index.d.ts +8 -0
  29. package/dist/core/auth/oauth/index.js +13 -15
  30. package/dist/core/auth/passwords.d.ts +15 -0
  31. package/dist/core/auth/passwords.js +32 -0
  32. package/dist/core/auth/redirects.d.ts +18 -0
  33. package/dist/core/auth/redirects.js +31 -0
  34. package/dist/core/auth/redirects.test.d.ts +1 -0
  35. package/dist/core/auth/routes.d.ts +4 -2
  36. package/dist/core/auth/routes.js +234 -9
  37. package/dist/core/auth/session.d.ts +11 -0
  38. package/dist/core/auth/session.js +1 -1
  39. package/dist/core/auth/spaceTokens.d.ts +38 -2
  40. package/dist/core/auth/spaceTokens.js +54 -2
  41. package/dist/core/auth/throttle.d.ts +5 -0
  42. package/dist/core/auth/throttle.js +81 -0
  43. package/dist/core/auth/throttle.test.d.ts +1 -0
  44. package/dist/core/auth/tokens.d.ts +83 -12
  45. package/dist/core/auth/tokens.js +51 -9
  46. package/dist/core/auth/totp.d.ts +48 -0
  47. package/dist/core/auth/totp.js +118 -0
  48. package/dist/core/auth/totp.test.d.ts +1 -0
  49. package/dist/core/createServer.d.ts +0 -6
  50. package/dist/core/createServer.js +35 -3
  51. package/dist/core/fontStore.d.ts +33 -0
  52. package/dist/core/fontStore.js +53 -0
  53. package/dist/core/fontStore.test.d.ts +1 -0
  54. package/dist/core/handlers/authMiddleware.js +1 -1
  55. package/dist/core/handlers/authRouteHandlers.d.ts +9 -1
  56. package/dist/core/handlers/authRouteHandlers.js +4 -3
  57. package/dist/core/handlers/csrfMiddleware.d.ts +20 -0
  58. package/dist/core/handlers/csrfMiddleware.js +32 -0
  59. package/dist/core/handlers/oauthRouteHandlers.d.ts +46 -0
  60. package/dist/core/handlers/oauthRouteHandlers.js +159 -0
  61. package/dist/core/handlers/socialRouteHandlers.d.ts +40 -0
  62. package/dist/core/handlers/socialRouteHandlers.js +129 -0
  63. package/dist/core/handlers/socialRouteHandlers.test.d.ts +1 -0
  64. package/dist/core/handlers/types.d.ts +22 -5
  65. package/dist/core/health.d.ts +11 -1
  66. package/dist/core/health.js +27 -3
  67. package/dist/core/http/dispatcher.d.ts +1 -1
  68. package/dist/core/http/dispatcher.js +20 -1
  69. package/dist/core/http/stages/authApi.js +1 -1
  70. package/dist/core/http/stages/fontAssets.d.ts +9 -0
  71. package/dist/core/http/stages/fontAssets.js +27 -0
  72. package/dist/core/http/stages/oauth.d.ts +19 -0
  73. package/dist/core/http/stages/oauth.js +118 -0
  74. package/dist/core/http/types.d.ts +12 -0
  75. package/dist/core/oauth/authorize.d.ts +22 -0
  76. package/dist/core/oauth/authorize.js +254 -0
  77. package/dist/core/oauth/authorize.test.d.ts +1 -0
  78. package/dist/core/oauth/challenge.d.ts +11 -0
  79. package/dist/core/oauth/challenge.js +33 -0
  80. package/dist/core/oauth/consentPage.d.ts +12 -0
  81. package/dist/core/oauth/consentPage.js +131 -0
  82. package/dist/core/oauth/metadata.d.ts +41 -0
  83. package/dist/core/oauth/metadata.js +71 -0
  84. package/dist/core/oauth/params.d.ts +5 -0
  85. package/dist/core/oauth/params.js +5 -0
  86. package/dist/core/oauth/pkce.d.ts +6 -0
  87. package/dist/core/oauth/pkce.js +15 -0
  88. package/dist/core/oauth/records.d.ts +72 -0
  89. package/dist/core/oauth/records.js +49 -0
  90. package/dist/core/oauth/register.d.ts +5 -0
  91. package/dist/core/oauth/register.js +55 -0
  92. package/dist/core/oauth/respond.d.ts +21 -0
  93. package/dist/core/oauth/respond.js +59 -0
  94. package/dist/core/oauth/token.d.ts +14 -0
  95. package/dist/core/oauth/token.js +130 -0
  96. package/dist/core/previewToken.d.ts +20 -0
  97. package/dist/core/previewToken.js +29 -1
  98. package/dist/core/server/pageServer.js +4 -1
  99. package/dist/core/services/action.d.ts +6 -4
  100. package/dist/core/services/action.js +103 -10
  101. package/dist/core/services/registry.js +2 -0
  102. package/dist/core/services/resolve.d.ts +8 -0
  103. package/dist/core/services/resolve.js +12 -1
  104. package/dist/core/staticFiles.js +1 -2
  105. package/dist/handlers.d.ts +7 -1
  106. package/dist/handlers.js +4 -1
  107. package/dist/helpers/buildResponseHelpers.d.ts +8 -0
  108. package/dist/helpers/buildResponseHelpers.js +9 -1
  109. package/dist/helpers/buildServerInfo.d.ts +8 -3
  110. package/dist/helpers/buildServerInfo.js +18 -14
  111. package/dist/helpers/cache/keys.d.ts +13 -0
  112. package/dist/helpers/cache/keys.js +12 -1
  113. package/dist/helpers/onAbort.d.ts +12 -0
  114. package/dist/helpers/onAbort.js +22 -0
  115. package/dist/helpers/onAbort.test.d.ts +1 -0
  116. package/dist/helpers/outboundGuard.d.ts +27 -0
  117. package/dist/helpers/outboundGuard.js +49 -0
  118. package/dist/helpers/outboundGuard.test.d.ts +1 -0
  119. package/dist/helpers/serverLog.d.ts +6 -3
  120. package/dist/helpers/serverLog.js +20 -3
  121. package/dist/index.d.ts +6 -0
  122. package/dist/index.js +9 -4
  123. package/dist/kernel.d.ts +5 -22
  124. package/dist/kernel.js +1 -14
  125. package/dist/modules/actions/{handler.js → connectorWrite.js} +5 -2
  126. package/dist/modules/actions/connectorWrite.test.d.ts +1 -0
  127. package/dist/modules/actions/index.d.ts +40 -0
  128. package/dist/modules/actions/index.js +46 -0
  129. package/dist/modules/actions/moduleFor.d.ts +3 -0
  130. package/dist/modules/actions/moduleFor.js +35 -0
  131. package/dist/modules/actions/moduleFor.test.d.ts +1 -0
  132. package/dist/modules/actions/runtime/check.d.ts +24 -0
  133. package/dist/modules/actions/runtime/check.js +111 -0
  134. package/dist/modules/actions/runtime/check.test.d.ts +1 -0
  135. package/dist/modules/actions/runtime/errors.d.ts +11 -0
  136. package/dist/modules/actions/runtime/errors.js +17 -0
  137. package/dist/modules/actions/runtime/guards.d.ts +126 -0
  138. package/dist/modules/actions/runtime/guards.js +246 -0
  139. package/dist/modules/actions/runtime/guards.test.d.ts +1 -0
  140. package/dist/modules/actions/runtime/kvStore.d.ts +27 -0
  141. package/dist/modules/actions/runtime/kvStore.js +41 -0
  142. package/dist/modules/actions/runtime/kvStore.test.d.ts +1 -0
  143. package/dist/modules/actions/runtime/limits.d.ts +11 -0
  144. package/dist/modules/actions/runtime/limits.js +35 -0
  145. package/dist/modules/actions/runtime/memoryKv.d.ts +2 -0
  146. package/dist/modules/actions/runtime/memoryKv.js +77 -0
  147. package/dist/modules/actions/runtime/namespaceKv.d.ts +9 -0
  148. package/dist/modules/actions/runtime/namespaceKv.js +19 -0
  149. package/dist/modules/actions/runtime/precheck.d.ts +27 -0
  150. package/dist/modules/actions/runtime/precheck.js +45 -0
  151. package/dist/modules/actions/runtime/renderResolver.d.ts +15 -0
  152. package/dist/modules/actions/runtime/renderResolver.js +130 -0
  153. package/dist/modules/actions/runtime/renderResolver.test.d.ts +1 -0
  154. package/dist/modules/actions/runtime/renderShare.d.ts +29 -0
  155. package/dist/modules/actions/runtime/renderShare.js +36 -0
  156. package/dist/modules/actions/runtime/renderShare.test.d.ts +1 -0
  157. package/dist/modules/actions/runtime/report.d.ts +14 -0
  158. package/dist/modules/actions/runtime/report.js +22 -0
  159. package/dist/modules/actions/runtime/runAction.d.ts +12 -0
  160. package/dist/modules/actions/runtime/runAction.js +405 -0
  161. package/dist/modules/actions/runtime/runAction.test.d.ts +1 -0
  162. package/dist/modules/actions/runtime/runLogger.d.ts +31 -0
  163. package/dist/modules/actions/runtime/runLogger.js +66 -0
  164. package/dist/modules/actions/runtime/runLogger.test.d.ts +1 -0
  165. package/dist/modules/actions/runtime/schedule.d.ts +35 -0
  166. package/dist/modules/actions/runtime/schedule.js +76 -0
  167. package/dist/modules/actions/runtime/schedule.test.d.ts +1 -0
  168. package/dist/modules/actions/runtime/scope.d.ts +40 -0
  169. package/dist/modules/actions/runtime/scope.js +103 -0
  170. package/dist/modules/actions/runtime/triggers.d.ts +12 -0
  171. package/dist/modules/actions/runtime/triggers.js +14 -0
  172. package/dist/modules/actions/taskCatalog.d.ts +26 -0
  173. package/dist/modules/actions/taskCatalog.js +24 -0
  174. package/dist/modules/actions/taskCatalog.test.d.ts +1 -0
  175. package/dist/modules/actions/tasks/auth.d.ts +2 -0
  176. package/dist/modules/actions/tasks/auth.js +40 -0
  177. package/dist/modules/actions/tasks/builtins.d.ts +8 -0
  178. package/dist/modules/actions/tasks/builtins.js +24 -0
  179. package/dist/modules/actions/tasks/connector.d.ts +2 -0
  180. package/dist/modules/actions/tasks/connector.js +141 -0
  181. package/dist/modules/actions/tasks/db.d.ts +2 -0
  182. package/dist/modules/actions/tasks/db.js +59 -0
  183. package/dist/modules/actions/tasks/db.test.d.ts +1 -0
  184. package/dist/modules/actions/tasks/flow.d.ts +3 -0
  185. package/dist/modules/actions/tasks/flow.js +109 -0
  186. package/dist/modules/actions/tasks/helpers.d.ts +16 -0
  187. package/dist/modules/actions/tasks/helpers.js +30 -0
  188. package/dist/modules/actions/tasks/http.d.ts +2 -0
  189. package/dist/modules/actions/tasks/http.js +111 -0
  190. package/dist/modules/actions/tasks/http.test.d.ts +1 -0
  191. package/dist/modules/actions/tasks/kv.d.ts +2 -0
  192. package/dist/modules/actions/tasks/kv.js +102 -0
  193. package/dist/modules/actions/tasks/kv.test.d.ts +1 -0
  194. package/dist/modules/actions/tasks/registry.d.ts +10 -0
  195. package/dist/modules/actions/tasks/registry.js +36 -0
  196. package/dist/modules/actions/tasks/transform.d.ts +2 -0
  197. package/dist/modules/actions/tasks/transform.js +34 -0
  198. package/dist/modules/actions/transport/callHandler.d.ts +24 -0
  199. package/dist/modules/actions/transport/callHandler.js +233 -0
  200. package/dist/modules/actions/transport/callHandler.test.d.ts +1 -0
  201. package/dist/modules/actions/transport/cancelHandler.d.ts +23 -0
  202. package/dist/modules/actions/transport/cancelHandler.js +23 -0
  203. package/dist/modules/actions/transport/catalogHandler.d.ts +18 -0
  204. package/dist/modules/actions/transport/catalogHandler.js +21 -0
  205. package/dist/modules/actions/transport/stream.d.ts +23 -0
  206. package/dist/modules/actions/transport/stream.js +100 -0
  207. package/dist/modules/actions/transport/verifySignature.d.ts +16 -0
  208. package/dist/modules/actions/transport/verifySignature.js +72 -0
  209. package/dist/modules/actions/transport/webhookHandler.d.ts +25 -0
  210. package/dist/modules/actions/transport/webhookHandler.js +206 -0
  211. package/dist/modules/actions/transport/webhookHandler.test.d.ts +1 -0
  212. package/dist/modules/actions/types.d.ts +234 -0
  213. package/dist/modules/connectors/engine.js +4 -1
  214. package/dist/modules/connectors/resolver.d.ts +10 -2
  215. package/dist/modules/connectors/resolver.js +8 -3
  216. package/dist/modules/mysql/accounts.d.ts +5 -0
  217. package/dist/modules/mysql/accounts.js +365 -0
  218. package/dist/modules/mysql/accounts.test.d.ts +1 -0
  219. package/dist/modules/mysql/admin.d.ts +58 -0
  220. package/dist/modules/mysql/admin.js +133 -0
  221. package/dist/modules/mysql/config.d.ts +82 -0
  222. package/dist/modules/mysql/config.js +30 -0
  223. package/dist/modules/mysql/identities.d.ts +35 -0
  224. package/dist/modules/mysql/identities.js +43 -0
  225. package/dist/modules/mysql/index.d.ts +67 -0
  226. package/dist/modules/mysql/index.js +62 -0
  227. package/dist/modules/mysql/pool.d.ts +3 -0
  228. package/dist/modules/mysql/pool.js +88 -0
  229. package/dist/modules/mysql/query.d.ts +12 -0
  230. package/dist/modules/mysql/query.js +21 -0
  231. package/dist/modules/mysql/schema.d.ts +43 -0
  232. package/dist/modules/mysql/schema.js +340 -0
  233. package/dist/modules/mysql/schema.test.d.ts +1 -0
  234. package/dist/modules/mysql/sessions.d.ts +33 -0
  235. package/dist/modules/mysql/sessions.js +46 -0
  236. package/dist/modules/mysql/spaceTokens.d.ts +11 -0
  237. package/dist/modules/mysql/spaceTokens.js +59 -0
  238. package/dist/modules/mysql/store.integration.test.d.ts +1 -0
  239. package/dist/modules/rsc/connectorRscData.d.ts +25 -0
  240. package/dist/modules/rsc/connectorRscData.js +51 -0
  241. package/dist/modules/rsc/connectorRscData.test.d.ts +1 -0
  242. package/dist/modules/rsc/handler.js +16 -3
  243. package/dist/modules/rsc/matchRscPage.d.ts +14 -0
  244. package/dist/modules/rsc/matchRscPage.js +26 -0
  245. package/dist/modules/rsc/resolveRscData.d.ts +14 -1
  246. package/dist/modules/rsc/resolveRscData.js +23 -34
  247. package/dist/modules/ssr/Component.d.ts +6 -2
  248. package/dist/modules/ssr/Component.js +3 -1
  249. package/dist/modules/ssr/draftSession.test.d.ts +1 -0
  250. package/dist/modules/ssr/handler.js +15 -2
  251. package/dist/modules/ssr/loadPluginComponents.js +22 -5
  252. package/dist/modules/ssr/prepareRender.js +87 -8
  253. package/dist/modules/ssr/prepareRender.test.d.ts +1 -0
  254. package/dist/modules/ssr/preview.d.ts +21 -8
  255. package/dist/modules/ssr/preview.js +38 -14
  256. package/dist/modules/ssr/registerExternalPlugins.js +2 -2
  257. package/dist/modules/ssr/resolvePageSeo.d.ts +18 -0
  258. package/dist/modules/ssr/resolvePageSeo.js +26 -0
  259. package/dist/modules/ssr/resolvePageSeo.test.d.ts +1 -0
  260. package/dist/modules/ssr/template.test.d.ts +1 -0
  261. package/dist/modules/ssr/views/template.ejs +71 -5
  262. package/dist/mysql.d.ts +26 -0
  263. package/dist/mysql.js +4 -0
  264. package/dist/oauth.d.ts +24 -0
  265. package/dist/oauth.js +10 -0
  266. package/dist/plugins/compile.d.ts +1 -0
  267. package/dist/plugins/compile.js +37 -3
  268. package/dist/plugins/manager.d.ts +40 -0
  269. package/dist/plugins/manager.js +113 -13
  270. package/dist/plugins/manager.test.d.ts +1 -0
  271. package/package.json +43 -14
  272. package/skills/plitzi-authoring/SKILL.md +252 -0
  273. /package/dist/{modules/actions/handler.test.d.ts → adapters/cloudAdapters.test.d.ts} +0 -0
  274. /package/dist/modules/actions/{handler.d.ts → connectorWrite.d.ts} +0 -0
@@ -0,0 +1,35 @@
1
+ import { Tables } from './config';
2
+ import { Queryable } from './query';
3
+ /** The same person at somebody else's identity provider. */
4
+ export interface LinkedIdentity {
5
+ id: number;
6
+ provider: string;
7
+ subject: string;
8
+ email?: string;
9
+ createdAt: number;
10
+ }
11
+ /**
12
+ * External identities, as rows.
13
+ *
14
+ * Deliberately NOT part of the auth adapters: whether a Google account with a matching email is the same person is
15
+ * a judgement only a deployment can make, and it lives in `exchangeCredential` / the social `linkAccount` hook.
16
+ * What is mechanical is the storage, and a self-hoster should not have to design the table for it.
17
+ *
18
+ * The unique key is `(provider, subject)`, never the email. A provider that does not verify addresses lets anybody
19
+ * claim any account by signing up with one — which is the single most common way social linking is got wrong.
20
+ */
21
+ export declare const createIdentityStore: (db: Queryable, t: Tables) => {
22
+ find: (provider: string, subject: string) => Promise<{
23
+ accountId: number;
24
+ } | undefined>;
25
+ /** Idempotent: signing in again with the same provider identity must not be an error. */
26
+ link: (accountId: number, identity: {
27
+ provider: string;
28
+ subject: string;
29
+ email?: string;
30
+ }) => Promise<void>;
31
+ list: (accountId: number) => Promise<LinkedIdentity[]>;
32
+ /** Scoped by account, or one person could unlink another's provider by guessing a number. */
33
+ unlink: (accountId: number, provider: string) => Promise<boolean>;
34
+ };
35
+ export type IdentityStore = ReturnType<typeof createIdentityStore>;
@@ -0,0 +1,43 @@
1
+ import { execute, selectOne, selectRows } from "./query.js";
2
+ //#region src/modules/mysql/identities.ts
3
+ /**
4
+ * External identities, as rows.
5
+ *
6
+ * Deliberately NOT part of the auth adapters: whether a Google account with a matching email is the same person is
7
+ * a judgement only a deployment can make, and it lives in `exchangeCredential` / the social `linkAccount` hook.
8
+ * What is mechanical is the storage, and a self-hoster should not have to design the table for it.
9
+ *
10
+ * The unique key is `(provider, subject)`, never the email. A provider that does not verify addresses lets anybody
11
+ * claim any account by signing up with one — which is the single most common way social linking is got wrong.
12
+ */
13
+ var createIdentityStore = (db, t) => ({
14
+ find: async (provider, subject) => {
15
+ const row = await selectOne(db, `SELECT account_id FROM ${t.identity} WHERE provider = ? AND subject = ? LIMIT 1`, [provider, subject]);
16
+ return row ? { accountId: row.account_id } : void 0;
17
+ },
18
+ /** Idempotent: signing in again with the same provider identity must not be an error. */
19
+ link: async (accountId, identity) => {
20
+ await execute(db, `INSERT INTO ${t.identity} (account_id, provider, subject, email) VALUES (?, ?, ?, ?)
21
+ ON DUPLICATE KEY UPDATE account_id = VALUES(account_id), email = VALUES(email)`, [
22
+ accountId,
23
+ identity.provider,
24
+ identity.subject,
25
+ identity.email ?? null
26
+ ]);
27
+ },
28
+ list: async (accountId) => {
29
+ return (await selectRows(db, `SELECT id, provider, subject, email, created_at FROM ${t.identity} WHERE account_id = ? ORDER BY id ASC`, [accountId])).map((row) => ({
30
+ id: row.id,
31
+ provider: row.provider,
32
+ subject: row.subject,
33
+ ...row.email ? { email: row.email } : {},
34
+ createdAt: Math.floor(row.created_at.getTime() / 1e3)
35
+ }));
36
+ },
37
+ /** Scoped by account, or one person could unlink another's provider by guessing a number. */
38
+ unlink: async (accountId, provider) => {
39
+ return (await execute(db, `DELETE FROM ${t.identity} WHERE account_id = ? AND provider = ?`, [accountId, provider])).affectedRows > 0;
40
+ }
41
+ });
42
+ //#endregion
43
+ export { createIdentityStore };
@@ -0,0 +1,67 @@
1
+ import { dropSchema } from './schema';
2
+ import { MysqlAdmin } from './admin';
3
+ import { MysqlConfig } from './config';
4
+ import { IdentityStore } from './identities';
5
+ import { SessionStore } from './sessions';
6
+ import { AccountAdapters } from '../../core/auth/api';
7
+ import { IdentityAdapters } from '../../core/auth/identity';
8
+ import { SpaceTokenAdapters } from '../../core/auth/spaceTokens';
9
+ import { Pool } from 'mysql2/promise';
10
+ export interface MysqlStore {
11
+ /** The pool, for a deployment with queries of its own. Ended by `close()` unless it was handed in. */
12
+ pool: Pool;
13
+ /** The resolved, unprefixed-plus-prefix table names, so a migration tool or an error can name one. */
14
+ tables: Record<string, string>;
15
+ schemaVersion: number;
16
+ /**
17
+ * Both halves of what `createAuth` asks for, implemented. Spread your own on top to add what only you can answer
18
+ * — `sendMail` for the reset and validation flows, `exchangeCredential` for an external identity provider.
19
+ */
20
+ authAdapters: IdentityAdapters & AccountAdapters;
21
+ /** For `createSpaceTokenApi`. Present whether or not this deployment issues space credentials. */
22
+ spaceTokenAdapters: SpaceTokenAdapters;
23
+ /** The account's signed-in devices: list them, end one, end the others. */
24
+ sessions: SessionStore;
25
+ /** Accounts at other identity providers. Storage only — whether two are the same person is your judgement. */
26
+ identities: IdentityStore;
27
+ /** Seeding and administration — roles, permissions, memberships, a space's first credential. */
28
+ admin: MysqlAdmin;
29
+ /**
30
+ * Removes this schema and nothing else — for a deployment that shared a database with other things and has
31
+ * stopped using it. Returns the tables it dropped. Refuses a database it was never migrated into.
32
+ */
33
+ uninstall: (options?: {
34
+ force?: boolean;
35
+ }) => Promise<string[]>;
36
+ close: () => Promise<void>;
37
+ }
38
+ /**
39
+ * An account store on MySQL, with the tables it needs.
40
+ *
41
+ * This is the batteries-included half of `createAuth`. A deployment that already has users implements the adapters
42
+ * against its own tables and never comes here; a deployment standing one up gets the schema, the queries and the
43
+ * traps already handled:
44
+ *
45
+ * ```ts
46
+ * const store = await createMysqlStore({ url: process.env.DATABASE_URL });
47
+ *
48
+ * const auth = createAuth({
49
+ * tokens: { secret, issuer },
50
+ * adapters: store.authAdapters,
51
+ * });
52
+ * ```
53
+ *
54
+ * It connects to a MySQL server; it does not start one. Where the database lives is a deployment decision and one
55
+ * this cannot make well — a container in development, a managed instance in production, and neither of them
56
+ * something a library should be launching behind your back.
57
+ */
58
+ export declare const createMysqlStore: (config: MysqlConfig) => Promise<MysqlStore>;
59
+ export { dropSchema };
60
+ /** The DDL, for a deployment whose database user may not run it — see `MysqlConfig.autoMigrate`. */
61
+ export declare const mysqlSchemaStatements: (tablePrefix: string, fromVersion?: number) => string[];
62
+ export { TABLE_NAMES, tableNames } from './config';
63
+ export { SCHEMA_VERSION } from './schema';
64
+ export type { MysqlConfig, TableKey, Tables } from './config';
65
+ export type { AccountSeed, MysqlAdmin, SpaceTokenSeed } from './admin';
66
+ export type { SessionStore, SessionSummary } from './sessions';
67
+ export type { IdentityStore, LinkedIdentity } from './identities';
@@ -0,0 +1,62 @@
1
+ import { createSessionStore } from "./sessions.js";
2
+ import { createAccountStore } from "./accounts.js";
3
+ import { createAdmin } from "./admin.js";
4
+ import { TABLE_NAMES, resolveTables, tableNames } from "./config.js";
5
+ import { createIdentityStore } from "./identities.js";
6
+ import { createPool } from "./pool.js";
7
+ import { SCHEMA_VERSION, dropSchema, migrate, schemaStatements } from "./schema.js";
8
+ import { createSpaceTokenStore } from "./spaceTokens.js";
9
+ //#region src/modules/mysql/index.ts
10
+ /**
11
+ * An account store on MySQL, with the tables it needs.
12
+ *
13
+ * This is the batteries-included half of `createAuth`. A deployment that already has users implements the adapters
14
+ * against its own tables and never comes here; a deployment standing one up gets the schema, the queries and the
15
+ * traps already handled:
16
+ *
17
+ * ```ts
18
+ * const store = await createMysqlStore({ url: process.env.DATABASE_URL });
19
+ *
20
+ * const auth = createAuth({
21
+ * tokens: { secret, issuer },
22
+ * adapters: store.authAdapters,
23
+ * });
24
+ * ```
25
+ *
26
+ * It connects to a MySQL server; it does not start one. Where the database lives is a deployment decision and one
27
+ * this cannot make well — a container in development, a managed instance in production, and neither of them
28
+ * something a library should be launching behind your back.
29
+ */
30
+ var createMysqlStore = async (config) => {
31
+ const { tablePrefix, autoMigrate = true, log } = config;
32
+ const tables = resolveTables(tablePrefix);
33
+ const pool = await createPool(config);
34
+ const owned = !config.pool;
35
+ if (autoMigrate) await migrate(pool, tables, {
36
+ prefix: tablePrefix,
37
+ adoptExisting: config.adoptExisting,
38
+ log
39
+ });
40
+ return {
41
+ pool,
42
+ tables: tableNames(tablePrefix),
43
+ schemaVersion: SCHEMA_VERSION,
44
+ authAdapters: createAccountStore(pool, tables),
45
+ spaceTokenAdapters: createSpaceTokenStore(pool, tables),
46
+ sessions: createSessionStore(pool, tables),
47
+ identities: createIdentityStore(pool, tables),
48
+ admin: createAdmin(pool, tables),
49
+ uninstall: (options = {}) => dropSchema(pool, tables, {
50
+ prefix: tablePrefix,
51
+ force: options.force,
52
+ log
53
+ }),
54
+ close: async () => {
55
+ if (owned) await pool.end();
56
+ }
57
+ };
58
+ };
59
+ /** The DDL, for a deployment whose database user may not run it — see `MysqlConfig.autoMigrate`. */
60
+ var mysqlSchemaStatements = (tablePrefix, fromVersion = 0) => schemaStatements(resolveTables(tablePrefix), fromVersion);
61
+ //#endregion
62
+ export { SCHEMA_VERSION, TABLE_NAMES, createMysqlStore, dropSchema, mysqlSchemaStatements, tableNames };
@@ -0,0 +1,3 @@
1
+ import { MysqlConfig } from './config';
2
+ import { Pool } from 'mysql2/promise';
3
+ export declare const createPool: (config: MysqlConfig) => Promise<Pool>;
@@ -0,0 +1,88 @@
1
+ //#region src/modules/mysql/pool.ts
2
+ var driver;
3
+ /**
4
+ * `mysql2` is an OPTIONAL peer dependency: this is the only module in the package that speaks to a database, and a
5
+ * deployment bringing its own store should not download a driver to find that out. Loaded dynamically so its absence
6
+ * is a sentence rather than a bare `ERR_MODULE_NOT_FOUND` from a file the reader has never heard of.
7
+ */
8
+ var loadDriver = async () => {
9
+ if (driver) return driver;
10
+ try {
11
+ driver = await import("mysql2/promise");
12
+ } catch {
13
+ throw new Error("@plitzi/sdk-server/mysql needs the `mysql2` driver, which is not installed. Add it with `yarn add mysql2` (or `npm install mysql2`). It is an optional peer dependency so that a deployment bringing its own account store never downloads a database driver it does not use.");
14
+ }
15
+ return driver;
16
+ };
17
+ /**
18
+ * The configured connection string, as the driver wants it.
19
+ *
20
+ * Parsed rather than handed to `createPool` as `uri`, and that is not a style choice: **that option is ignored** —
21
+ * only `createPool(uriString)` reads a URL, and that form takes no options. Passing both silently drops the URL and
22
+ * connects to the driver's own defaults, which on a machine that happens to run MySQL locally is a connection to
23
+ * the wrong database rather than an error. Anything unparseable is refused here, where the message can say so.
24
+ */
25
+ var fromUrl = (url) => {
26
+ let parsed;
27
+ try {
28
+ parsed = new URL(url);
29
+ } catch {
30
+ throw new Error("@plitzi/sdk-server/mysql: `url` is not a valid database URL");
31
+ }
32
+ const database = parsed.pathname.replace(/^\//, "");
33
+ if (!parsed.hostname) throw new Error("@plitzi/sdk-server/mysql: `url` names no host");
34
+ return {
35
+ host: parsed.hostname,
36
+ port: parsed.port ? Number(parsed.port) : 3306,
37
+ user: decodeURIComponent(parsed.username),
38
+ password: decodeURIComponent(parsed.password),
39
+ database
40
+ };
41
+ };
42
+ var createPool = async (config) => {
43
+ if (config.pool) return config.pool;
44
+ const { url, host, port, user, password, database, ssl, connectionLimit = 10 } = config;
45
+ const options = {
46
+ ...url ? fromUrl(url) : {},
47
+ ...host !== void 0 ? { host } : {},
48
+ ...port !== void 0 ? { port } : {},
49
+ ...user !== void 0 ? { user } : {},
50
+ ...password !== void 0 ? { password } : {},
51
+ ...database !== void 0 ? { database } : {},
52
+ ...ssl !== void 0 ? { ssl } : {},
53
+ connectionLimit,
54
+ supportBigNumbers: true,
55
+ bigNumberStrings: false,
56
+ waitForConnections: true,
57
+ queueLimit: 0
58
+ };
59
+ if (!options.database) throw new Error("@plitzi/sdk-server/mysql: no database was given. Pass `database`, or a `url` that ends in one — a connection with no database selected fails later, on the first query, as a much less obvious error.");
60
+ const mysql = await loadDriver();
61
+ if (config.ensureDatabase) await createDatabase(mysql, options);
62
+ return mysql.createPool(options);
63
+ };
64
+ /**
65
+ * A database name cannot be a bound parameter — it is an identifier, not a value — so it is quoted by hand, and
66
+ * anything that could end the quoting is refused rather than escaped. The set of legal MySQL database names is
67
+ * small and this is all of it.
68
+ */
69
+ var quoteDatabase = (name) => {
70
+ if (!/^[A-Za-z0-9_$]+$/.test(name)) throw new Error(`@plitzi/sdk-server/mysql: "${name}" is not a database name that can be created safely. Use letters, digits, underscores and dollar signs, or create the database yourself and leave \`ensureDatabase\` off.`);
71
+ return `\`${name}\``;
72
+ };
73
+ /** Connects to the SERVER rather than to the database, which is the only way to ask for one that is not there. */
74
+ var createDatabase = async (mysql, options) => {
75
+ const { database, ...server } = options;
76
+ const name = quoteDatabase(database ?? "");
77
+ const connection = await mysql.createConnection(server);
78
+ try {
79
+ await connection.query(`CREATE DATABASE IF NOT EXISTS ${name} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
80
+ } catch (error) {
81
+ if (error.code === "ER_DBACCESS_DENIED_ERROR") throw new Error(`@plitzi/sdk-server/mysql: the user "${String(options.user)}" may not create the database ${name}. Create it once as an administrator — CREATE DATABASE ${name} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; — and grant that user rights on it, then turn \`ensureDatabase\` off. Creating databases is not a right an application should hold in the first place.`, { cause: error });
82
+ throw error;
83
+ } finally {
84
+ await connection.end();
85
+ }
86
+ };
87
+ //#endregion
88
+ export { createPool };
@@ -0,0 +1,12 @@
1
+ import { Pool, PoolConnection, ResultSetHeader } from 'mysql2/promise';
2
+ export type Queryable = Pool | PoolConnection;
3
+ /**
4
+ * The driver boundary, in one place.
5
+ *
6
+ * A row arrives as `RowDataPacket` — an index signature over `unknown`, which is all the driver can honestly say
7
+ * about a result set it has not been told the shape of. Naming that shape is a cast whichever way it is written, so
8
+ * it is written once, here, rather than at each of the thirty call sites that would otherwise each carry one.
9
+ */
10
+ export declare const selectRows: <T>(db: Queryable, sql: string, params?: unknown[]) => Promise<T[]>;
11
+ export declare const selectOne: <T>(db: Queryable, sql: string, params?: unknown[]) => Promise<T | undefined>;
12
+ export declare const execute: (db: Queryable, sql: string, params?: unknown[]) => Promise<ResultSetHeader>;
@@ -0,0 +1,21 @@
1
+ //#region src/modules/mysql/query.ts
2
+ /**
3
+ * The driver boundary, in one place.
4
+ *
5
+ * A row arrives as `RowDataPacket` — an index signature over `unknown`, which is all the driver can honestly say
6
+ * about a result set it has not been told the shape of. Naming that shape is a cast whichever way it is written, so
7
+ * it is written once, here, rather than at each of the thirty call sites that would otherwise each carry one.
8
+ */
9
+ var selectRows = async (db, sql, params = []) => {
10
+ const [rows] = await db.query(sql, params);
11
+ return rows;
12
+ };
13
+ var selectOne = async (db, sql, params = []) => {
14
+ return (await selectRows(db, sql, params))[0];
15
+ };
16
+ var execute = async (db, sql, params = []) => {
17
+ const [result] = await db.query(sql, params);
18
+ return result;
19
+ };
20
+ //#endregion
21
+ export { execute, selectOne, selectRows };
@@ -0,0 +1,43 @@
1
+ import { Tables } from './config';
2
+ import { Pool } from 'mysql2/promise';
3
+ /** Adds a column unless it is already there. `definition` is everything after the column name. */
4
+ export declare const addColumn: (table: string, column: string, definition: string) => string[];
5
+ /** Removes a column if it is there. Data goes with it — a step that does this is not reversible. */
6
+ export declare const dropColumn: (table: string, column: string) => string[];
7
+ /** Adds an index unless one of that name exists. `definition` is the parenthesised column list. */
8
+ export declare const addIndex: (table: string, index: string, definition: string) => string[];
9
+ export declare const SCHEMA_VERSION: number;
10
+ /**
11
+ * Every statement this schema is made of, in order — for a deployment that runs `autoMigrate: false` and applies
12
+ * DDL through its own migration tool, which is the normal arrangement anywhere the application's database user is
13
+ * not allowed to alter tables.
14
+ */
15
+ export declare const schemaStatements: (tables: Tables, fromVersion?: number) => string[];
16
+ /**
17
+ * Brings the schema up to date, once.
18
+ *
19
+ * The advisory lock is not decoration: an all-in-one deployment starts several roles against one database at the
20
+ * same moment, and without it they race to create the same tables and to bump the same version row. `IF NOT EXISTS`
21
+ * makes the statements survive that; the version bump would not.
22
+ */
23
+ export declare const migrate: (pool: Pool, tables: Tables, options: {
24
+ prefix: string;
25
+ adoptExisting?: boolean;
26
+ log?: (message: string) => void;
27
+ }) => Promise<number>;
28
+ /**
29
+ * Removes this schema and nothing else.
30
+ *
31
+ * For a deployment that pointed the store at a database it shares with other things and has now stopped using
32
+ * `@plitzi/sdk-server` — dropping the database would take the rest of their application with it, and working out
33
+ * which nine tables were ours is not something anybody should have to do by hand at that moment.
34
+ *
35
+ * It drops **only** the names this schema owns, children first so the foreign keys allow it, and it refuses to
36
+ * touch a database it was never migrated into: without that check, a typo in `tablePrefix` turns this into a tool
37
+ * that deletes somebody else's `role` table.
38
+ */
39
+ export declare const dropSchema: (pool: Pool, tables: Tables, options: {
40
+ prefix: string;
41
+ force?: boolean;
42
+ log?: (message: string) => void;
43
+ }) => Promise<string[]>;