@headless-lms/server 0.1.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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/app/container.d.ts +124 -0
  4. package/dist/app/container.js +312 -0
  5. package/dist/app/container.js.map +1 -0
  6. package/dist/app/db.d.ts +2 -0
  7. package/dist/app/db.js +2 -0
  8. package/dist/app/integrations.d.ts +7 -0
  9. package/dist/app/integrations.js +44 -0
  10. package/dist/app/integrations.js.map +1 -0
  11. package/dist/app/notifications.d.ts +7 -0
  12. package/dist/app/notifications.js +23 -0
  13. package/dist/app/notifications.js.map +1 -0
  14. package/dist/http/config.d.ts +25 -0
  15. package/dist/http/config.js +1 -0
  16. package/dist/http/fastify.d.ts +35 -0
  17. package/dist/http/plugins/auth.d.ts +11 -0
  18. package/dist/http/plugins/auth.js +47 -0
  19. package/dist/http/plugins/auth.js.map +1 -0
  20. package/dist/http/plugins/cors.d.ts +7 -0
  21. package/dist/http/plugins/cors.js +22 -0
  22. package/dist/http/plugins/cors.js.map +1 -0
  23. package/dist/http/plugins/error-handler.d.ts +6 -0
  24. package/dist/http/plugins/error-handler.js +64 -0
  25. package/dist/http/plugins/error-handler.js.map +1 -0
  26. package/dist/http/plugins/openapi.d.ts +7 -0
  27. package/dist/http/plugins/openapi.js +63 -0
  28. package/dist/http/plugins/openapi.js.map +1 -0
  29. package/dist/http/routes/activities.d.ts +7 -0
  30. package/dist/http/routes/activities.js +163 -0
  31. package/dist/http/routes/activities.js.map +1 -0
  32. package/dist/http/routes/assets.d.ts +7 -0
  33. package/dist/http/routes/assets.js +168 -0
  34. package/dist/http/routes/assets.js.map +1 -0
  35. package/dist/http/routes/automations.d.ts +7 -0
  36. package/dist/http/routes/automations.js +158 -0
  37. package/dist/http/routes/automations.js.map +1 -0
  38. package/dist/http/routes/courses.d.ts +7 -0
  39. package/dist/http/routes/courses.js +130 -0
  40. package/dist/http/routes/courses.js.map +1 -0
  41. package/dist/http/routes/dashboard.d.ts +7 -0
  42. package/dist/http/routes/dashboard.js +42 -0
  43. package/dist/http/routes/dashboard.js.map +1 -0
  44. package/dist/http/routes/discussion.d.ts +7 -0
  45. package/dist/http/routes/discussion.js +122 -0
  46. package/dist/http/routes/discussion.js.map +1 -0
  47. package/dist/http/routes/downloads.d.ts +7 -0
  48. package/dist/http/routes/downloads.js +208 -0
  49. package/dist/http/routes/downloads.js.map +1 -0
  50. package/dist/http/routes/entitlements.d.ts +7 -0
  51. package/dist/http/routes/entitlements.js +69 -0
  52. package/dist/http/routes/entitlements.js.map +1 -0
  53. package/dist/http/routes/identity.d.ts +6 -0
  54. package/dist/http/routes/identity.js +6 -0
  55. package/dist/http/routes/identity.js.map +1 -0
  56. package/dist/http/routes/integrations.d.ts +7 -0
  57. package/dist/http/routes/integrations.js +210 -0
  58. package/dist/http/routes/integrations.js.map +1 -0
  59. package/dist/http/routes/learn/assets.d.ts +8 -0
  60. package/dist/http/routes/learn/assets.js +32 -0
  61. package/dist/http/routes/learn/assets.js.map +1 -0
  62. package/dist/http/routes/learn/comments.d.ts +7 -0
  63. package/dist/http/routes/learn/comments.js +175 -0
  64. package/dist/http/routes/learn/comments.js.map +1 -0
  65. package/dist/http/routes/learn/courses.d.ts +7 -0
  66. package/dist/http/routes/learn/courses.js +75 -0
  67. package/dist/http/routes/learn/courses.js.map +1 -0
  68. package/dist/http/routes/learn/downloads.d.ts +7 -0
  69. package/dist/http/routes/learn/downloads.js +76 -0
  70. package/dist/http/routes/learn/downloads.js.map +1 -0
  71. package/dist/http/routes/learn/index.d.ts +7 -0
  72. package/dist/http/routes/learn/index.js +19 -0
  73. package/dist/http/routes/learn/index.js.map +1 -0
  74. package/dist/http/routes/learn/org.d.ts +7 -0
  75. package/dist/http/routes/learn/org.js +50 -0
  76. package/dist/http/routes/learn/org.js.map +1 -0
  77. package/dist/http/routes/learn/progress.d.ts +7 -0
  78. package/dist/http/routes/learn/progress.js +63 -0
  79. package/dist/http/routes/learn/progress.js.map +1 -0
  80. package/dist/http/routes/organizations.d.ts +7 -0
  81. package/dist/http/routes/organizations.js +219 -0
  82. package/dist/http/routes/organizations.js.map +1 -0
  83. package/dist/http/routes/progress.d.ts +6 -0
  84. package/dist/http/routes/progress.js +6 -0
  85. package/dist/http/routes/progress.js.map +1 -0
  86. package/dist/http/routes/students.d.ts +7 -0
  87. package/dist/http/routes/students.js +125 -0
  88. package/dist/http/routes/students.js.map +1 -0
  89. package/dist/http/routes.d.ts +7 -0
  90. package/dist/http/routes.js +37 -0
  91. package/dist/http/routes.js.map +1 -0
  92. package/dist/http/schemas/activities.d.ts +69 -0
  93. package/dist/http/schemas/activities.js +25 -0
  94. package/dist/http/schemas/activities.js.map +1 -0
  95. package/dist/http/schemas/assets.d.ts +142 -0
  96. package/dist/http/schemas/assets.js +24 -0
  97. package/dist/http/schemas/assets.js.map +1 -0
  98. package/dist/http/schemas/automations.d.ts +154 -0
  99. package/dist/http/schemas/automations.js +26 -0
  100. package/dist/http/schemas/automations.js.map +1 -0
  101. package/dist/http/schemas/content.d.ts +113 -0
  102. package/dist/http/schemas/content.js +29 -0
  103. package/dist/http/schemas/content.js.map +1 -0
  104. package/dist/http/schemas/dashboard.d.ts +26 -0
  105. package/dist/http/schemas/dashboard.js +18 -0
  106. package/dist/http/schemas/dashboard.js.map +1 -0
  107. package/dist/http/schemas/discussion.d.ts +340 -0
  108. package/dist/http/schemas/discussion.js +78 -0
  109. package/dist/http/schemas/discussion.js.map +1 -0
  110. package/dist/http/schemas/downloads.d.ts +110 -0
  111. package/dist/http/schemas/downloads.js +34 -0
  112. package/dist/http/schemas/downloads.js.map +1 -0
  113. package/dist/http/schemas/entitlements.d.ts +97 -0
  114. package/dist/http/schemas/entitlements.js +41 -0
  115. package/dist/http/schemas/entitlements.js.map +1 -0
  116. package/dist/http/schemas/index.d.ts +17 -0
  117. package/dist/http/schemas/index.js +17 -0
  118. package/dist/http/schemas/integrations.d.ts +93 -0
  119. package/dist/http/schemas/integrations.js +47 -0
  120. package/dist/http/schemas/integrations.js.map +1 -0
  121. package/dist/http/schemas/invites.d.ts +95 -0
  122. package/dist/http/schemas/invites.js +62 -0
  123. package/dist/http/schemas/invites.js.map +1 -0
  124. package/dist/http/schemas/learn.d.ts +142 -0
  125. package/dist/http/schemas/learn.js +58 -0
  126. package/dist/http/schemas/learn.js.map +1 -0
  127. package/dist/http/schemas/members.d.ts +88 -0
  128. package/dist/http/schemas/members.js +27 -0
  129. package/dist/http/schemas/members.js.map +1 -0
  130. package/dist/http/schemas/organizations.d.ts +22 -0
  131. package/dist/http/schemas/organizations.js +20 -0
  132. package/dist/http/schemas/organizations.js.map +1 -0
  133. package/dist/http/schemas/settings.d.ts +23 -0
  134. package/dist/http/schemas/settings.js +20 -0
  135. package/dist/http/schemas/settings.js.map +1 -0
  136. package/dist/http/schemas/shared.d.ts +57 -0
  137. package/dist/http/schemas/shared.js +54 -0
  138. package/dist/http/schemas/shared.js.map +1 -0
  139. package/dist/http/schemas/students.d.ts +64 -0
  140. package/dist/http/schemas/students.js +25 -0
  141. package/dist/http/schemas/students.js.map +1 -0
  142. package/dist/http/scope.d.ts +22 -0
  143. package/dist/http/scope.js +28 -0
  144. package/dist/http/scope.js.map +1 -0
  145. package/dist/http/server.d.ts +8 -0
  146. package/dist/http/server.js +33 -0
  147. package/dist/http/server.js.map +1 -0
  148. package/dist/http/web-bridge.d.ts +9 -0
  149. package/dist/http/web-bridge.js +27 -0
  150. package/dist/http/web-bridge.js.map +1 -0
  151. package/dist/index.d.ts +15 -0
  152. package/dist/index.js +16 -0
  153. package/dist/index.js.map +1 -0
  154. package/package.json +56 -0
@@ -0,0 +1,22 @@
1
+ import { Container } from "../app/container.js";
2
+ import { StaffRole } from "@headless-lms/core/organizations";
3
+ import { FastifyRequest } from "fastify";
4
+ //#region src/http/scope.d.ts
5
+ interface OrgScope {
6
+ /** Domain `organizations.id` for the session's active org. */
7
+ orgId: string;
8
+ /** Domain `users.id` of the acting person. */
9
+ userId: string;
10
+ /** Domain `org_users.id` — the acting org user in this org. */
11
+ orgUserId: string;
12
+ /** The role held in this org. */
13
+ role: StaffRole;
14
+ /** Better-auth organization id (for writes that go through the auth provider). */
15
+ authOrgId: string;
16
+ }
17
+ /** Thrown when the session has no resolvable active org / domain user. */
18
+ declare class NoActiveOrgError extends Error {}
19
+ declare function resolveScope(container: Container, req: FastifyRequest): Promise<OrgScope>;
20
+ //#endregion
21
+ export { NoActiveOrgError, OrgScope, resolveScope };
22
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { isStaffRole } from "@headless-lms/core/organizations";
2
+ //#region src/http/scope.ts
3
+ /** Thrown when the session has no resolvable active org / domain user. */
4
+ var NoActiveOrgError = class extends Error {};
5
+ async function resolveScope(container, req) {
6
+ const authUser = req.authUser;
7
+ const authOrgId = req.authOrgId ?? null;
8
+ if (!authUser) throw new NoActiveOrgError("no authenticated user");
9
+ if (!authOrgId || !req.orgId) throw new NoActiveOrgError("no active organization in session");
10
+ const org = await container.organizations.getById(req.orgId);
11
+ if (!org) throw new NoActiveOrgError("active organization not found");
12
+ const user = await container.identity.getUserByExternalId(authUser.id);
13
+ if (!user) throw new NoActiveOrgError("no domain user for the current user");
14
+ const orgUser = await container.organizations.getOrgUser(org.id, user.id);
15
+ if (!orgUser || !isStaffRole(orgUser.role)) throw new NoActiveOrgError("not a staff member of the active organization");
16
+ container.requestContext.set({ orgId: org.id });
17
+ return {
18
+ orgId: org.id,
19
+ userId: user.id,
20
+ orgUserId: orgUser.id,
21
+ role: orgUser.role,
22
+ authOrgId
23
+ };
24
+ }
25
+ //#endregion
26
+ export { NoActiveOrgError, resolveScope };
27
+
28
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","names":[],"sources":["../../src/http/scope.ts"],"sourcesContent":["import type { FastifyRequest } from 'fastify';\nimport type { Container } from '../app/container.js';\nimport { isStaffRole, type StaffRole } from '@headless-lms/core/organizations';\n\nexport interface OrgScope {\n /** Domain `organizations.id` for the session's active org. */\n orgId: string;\n /** Domain `users.id` of the acting person. */\n userId: string;\n /** Domain `org_users.id` — the acting org user in this org. */\n orgUserId: string;\n /** The role held in this org. */\n role: StaffRole;\n /** Better-auth organization id (for writes that go through the auth provider). */\n authOrgId: string;\n}\n\n/** Thrown when the session has no resolvable active org / domain user. */\nexport class NoActiveOrgError extends Error {}\n\nexport async function resolveScope(container: Container, req: FastifyRequest): Promise<OrgScope> {\n const authUser = req.authUser;\n const authOrgId = req.authOrgId ?? null;\n if (!authUser) {\n throw new NoActiveOrgError('no authenticated user');\n }\n if (!authOrgId || !req.orgId) {\n throw new NoActiveOrgError('no active organization in session');\n }\n // The session guard already translated the auth org id to the domain org.\n const org = await container.organizations.getById(req.orgId);\n if (!org) {\n throw new NoActiveOrgError('active organization not found');\n }\n const user = await container.identity.getUserByExternalId(authUser.id);\n if (!user) {\n throw new NoActiveOrgError('no domain user for the current user');\n }\n const orgUser = await container.organizations.getOrgUser(org.id, user.id);\n if (!orgUser || !isStaffRole(orgUser.role)) {\n throw new NoActiveOrgError('not a staff member of the active organization');\n }\n container.requestContext.set({ orgId: org.id });\n return {\n orgId: org.id,\n userId: user.id,\n orgUserId: orgUser.id,\n role: orgUser.role,\n authOrgId,\n };\n}\n"],"mappings":";;;AAkBA,IAAa,mBAAb,cAAsC,MAAM,CAAC;AAE7C,eAAsB,aAAa,WAAsB,KAAwC;CAC/F,MAAM,WAAW,IAAI;CACrB,MAAM,YAAY,IAAI,aAAa;CACnC,IAAI,CAAC,UACH,MAAM,IAAI,iBAAiB,uBAAuB;CAEpD,IAAI,CAAC,aAAa,CAAC,IAAI,OACrB,MAAM,IAAI,iBAAiB,mCAAmC;CAGhE,MAAM,MAAM,MAAM,UAAU,cAAc,QAAQ,IAAI,KAAK;CAC3D,IAAI,CAAC,KACH,MAAM,IAAI,iBAAiB,+BAA+B;CAE5D,MAAM,OAAO,MAAM,UAAU,SAAS,oBAAoB,SAAS,EAAE;CACrE,IAAI,CAAC,MACH,MAAM,IAAI,iBAAiB,qCAAqC;CAElE,MAAM,UAAU,MAAM,UAAU,cAAc,WAAW,IAAI,IAAI,KAAK,EAAE;CACxE,IAAI,CAAC,WAAW,CAAC,YAAY,QAAQ,IAAI,GACvC,MAAM,IAAI,iBAAiB,+CAA+C;CAE5E,UAAU,eAAe,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC;CAC9C,OAAO;EACL,OAAO,IAAI;EACX,QAAQ,KAAK;EACb,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd;CACF;AACF"}
@@ -0,0 +1,8 @@
1
+ import { Container } from "../app/container.js";
2
+ import { ServerConfig } from "./config.js";
3
+ import { FastifyInstance } from "fastify";
4
+ //#region src/http/server.d.ts
5
+ declare function buildServer(config: ServerConfig, container: Container): Promise<FastifyInstance>;
6
+ //#endregion
7
+ export { buildServer };
8
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { registerCors } from "./plugins/cors.js";
2
+ import { registerOpenApi } from "./plugins/openapi.js";
3
+ import { registerAuth } from "./plugins/auth.js";
4
+ import { errorHandler } from "./plugins/error-handler.js";
5
+ import { registerRoutes } from "./routes.js";
6
+ import Fastify from "fastify";
7
+ import { serializerCompiler, validatorCompiler } from "fastify-type-provider-zod";
8
+ //#region src/http/server.ts
9
+ async function buildServer(config, container) {
10
+ const app = Fastify({
11
+ loggerInstance: container.loggerInstance,
12
+ disableRequestLogging: config.requestLogging === false
13
+ });
14
+ app.addHook("onRequest", (request, _reply, done) => {
15
+ container.requestContext.run({ reqId: request.id }, done);
16
+ });
17
+ app.setValidatorCompiler(validatorCompiler);
18
+ app.setSerializerCompiler(serializerCompiler);
19
+ app.setErrorHandler(errorHandler);
20
+ registerCors(app, config);
21
+ registerOpenApi(app, config);
22
+ registerAuth(app, container);
23
+ registerRoutes(app, container);
24
+ app.addHook("onClose", async () => {
25
+ await container.outboxRelay.stop();
26
+ await container.automationEngine.stop();
27
+ });
28
+ return app;
29
+ }
30
+ //#endregion
31
+ export { buildServer };
32
+
33
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/http/server.ts"],"sourcesContent":["import Fastify, { type FastifyBaseLogger, type FastifyInstance } from 'fastify';\nimport { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod';\nimport type { Container } from '../app/container.js';\nimport type { ServerConfig } from './config.js';\nimport { registerCors } from './plugins/cors.js';\nimport { registerOpenApi } from './plugins/openapi.js';\nimport { registerAuth } from './plugins/auth.js';\nimport { errorHandler } from './plugins/error-handler.js';\nimport { registerRoutes } from './routes.js';\n\nexport async function buildServer(\n config: ServerConfig,\n container: Container,\n): Promise<FastifyInstance> {\n // One log stream for the whole process: HTTP shares the container's pino root.\n // Widened to FastifyBaseLogger so the instance keeps the default logger\n // generic (a concrete pino type would ripple through every plugin signature).\n const app = Fastify({\n loggerInstance: container.loggerInstance as FastifyBaseLogger,\n // Fastify's own request/response pair per call — noisy in a dev console,\n // useful as an access log in production. Our own logs are unaffected.\n disableRequestLogging: config.requestLogging === false,\n });\n\n // Enter the request-scoped log context: every line logged inside this\n // request's async scope carries its reqId (and orgId once resolveScope runs),\n // wherever it is emitted.\n app.addHook('onRequest', (request, _reply, done) => {\n container.requestContext.run({ reqId: request.id }, done);\n });\n\n // Validate + serialize request/response bodies from the shared Zod contract.\n app.setValidatorCompiler(validatorCompiler);\n app.setSerializerCompiler(serializerCompiler);\n app.setErrorHandler(errorHandler);\n\n registerCors(app, config);\n registerOpenApi(app, config);\n registerAuth(app, container);\n\n registerRoutes(app, container);\n\n // Drain + stop the outbox relay and the automation engine on shutdown.\n // Harmless when neither was started (gen-openapi boots this app with\n // ready() + close() only).\n app.addHook('onClose', async () => {\n await container.outboxRelay.stop();\n await container.automationEngine.stop();\n });\n\n return app;\n}\n"],"mappings":";;;;;;;;AAUA,eAAsB,YACpB,QACA,WAC0B;CAI1B,MAAM,MAAM,QAAQ;EAClB,gBAAgB,UAAU;EAG1B,uBAAuB,OAAO,mBAAmB;CACnD,CAAC;CAKD,IAAI,QAAQ,cAAc,SAAS,QAAQ,SAAS;EAClD,UAAU,eAAe,IAAI,EAAE,OAAO,QAAQ,GAAG,GAAG,IAAI;CAC1D,CAAC;CAGD,IAAI,qBAAqB,iBAAiB;CAC1C,IAAI,sBAAsB,kBAAkB;CAC5C,IAAI,gBAAgB,YAAY;CAEhC,aAAa,KAAK,MAAM;CACxB,gBAAgB,KAAK,MAAM;CAC3B,aAAa,KAAK,SAAS;CAE3B,eAAe,KAAK,SAAS;CAK7B,IAAI,QAAQ,WAAW,YAAY;EACjC,MAAM,UAAU,YAAY,KAAK;EACjC,MAAM,UAAU,iBAAiB,KAAK;CACxC,CAAC;CAED,OAAO;AACT"}
@@ -0,0 +1,9 @@
1
+ import { FastifyReply, FastifyRequest } from "fastify";
2
+ //#region src/http/web-bridge.d.ts
3
+ /** Builds a Web `Request` from an incoming Fastify request. */
4
+ declare function toWebRequest(request: FastifyRequest): Request;
5
+ /** Copies a Web `Response` back onto a Fastify reply, preserving multiple cookies. */
6
+ declare function bridgeWebResponse(response: Response, reply: FastifyReply): Promise<void>;
7
+ //#endregion
8
+ export { bridgeWebResponse, toWebRequest };
9
+ //# sourceMappingURL=web-bridge.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { fromNodeHeaders } from "better-auth/node";
2
+ //#region src/http/web-bridge.ts
3
+ /** Builds a Web `Request` from an incoming Fastify request. */
4
+ function toWebRequest(request) {
5
+ const url = new URL(request.url, `http://${request.headers.host}`);
6
+ const body = request.method !== "GET" && request.method !== "HEAD" && request.method !== "DELETE" ? typeof request.body === "string" ? request.body : request.body ? JSON.stringify(request.body) : void 0 : void 0;
7
+ return new Request(url.toString(), {
8
+ method: request.method,
9
+ headers: fromNodeHeaders(request.headers),
10
+ body
11
+ });
12
+ }
13
+ /** Copies a Web `Response` back onto a Fastify reply, preserving multiple cookies. */
14
+ async function bridgeWebResponse(response, reply) {
15
+ reply.status(response.status);
16
+ const cookies = response.headers.getSetCookie();
17
+ if (cookies.length > 0) reply.raw.setHeader("set-cookie", cookies);
18
+ response.headers.forEach((value, key) => {
19
+ if (key.toLowerCase() === "set-cookie") return;
20
+ reply.header(key, value);
21
+ });
22
+ reply.send(response.body ? await response.text() : null);
23
+ }
24
+ //#endregion
25
+ export { bridgeWebResponse, toWebRequest };
26
+
27
+ //# sourceMappingURL=web-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-bridge.js","names":[],"sources":["../../src/http/web-bridge.ts"],"sourcesContent":["// Bridges between Fastify's request/reply and the Web `Request`/`Response` that\n// better-auth speaks. The auth catch-all hands a Fastify request to a\n// Web-standard handler and forwards the\n// Web response back, so the conversion lives here once.\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { fromNodeHeaders } from 'better-auth/node';\n\n/** Builds a Web `Request` from an incoming Fastify request. */\nexport function toWebRequest(request: FastifyRequest): Request {\n const url = new URL(request.url, `http://${request.headers.host}`);\n // GET/HEAD/DELETE never carry a body; passing one to `Request` throws. For the\n // rest, forward a string body verbatim and JSON-encode a parsed object.\n const hasBody =\n request.method !== 'GET' && request.method !== 'HEAD' && request.method !== 'DELETE';\n const body = hasBody\n ? typeof request.body === 'string'\n ? request.body\n : request.body\n ? JSON.stringify(request.body)\n : undefined\n : undefined;\n return new Request(url.toString(), {\n method: request.method,\n headers: fromNodeHeaders(request.headers),\n body,\n });\n}\n\n/** Copies a Web `Response` back onto a Fastify reply, preserving multiple cookies. */\nexport async function bridgeWebResponse(response: Response, reply: FastifyReply): Promise<void> {\n reply.status(response.status);\n // Emit each Set-Cookie value individually — forEach() collapses multiple\n // Set-Cookie headers into one comma-joined string (undici behaviour), which\n // corrupts multi-cookie auth responses. getSetCookie() returns them as an\n // array so each one is forwarded as a separate header.\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) {\n reply.raw.setHeader('set-cookie', cookies);\n }\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'set-cookie') {\n return;\n } // already handled above\n reply.header(key, value);\n });\n reply.send(response.body ? await response.text() : null);\n}\n"],"mappings":";;;AAQA,SAAgB,aAAa,SAAkC;CAC7D,MAAM,MAAM,IAAI,IAAI,QAAQ,KAAK,UAAU,QAAQ,QAAQ,MAAM;CAKjE,MAAM,OADJ,QAAQ,WAAW,SAAS,QAAQ,WAAW,UAAU,QAAQ,WAAW,WAE1E,OAAO,QAAQ,SAAS,WACtB,QAAQ,OACR,QAAQ,OACN,KAAK,UAAU,QAAQ,IAAI,IAC3B,KAAA,IACJ,KAAA;CACJ,OAAO,IAAI,QAAQ,IAAI,SAAS,GAAG;EACjC,QAAQ,QAAQ;EAChB,SAAS,gBAAgB,QAAQ,OAAO;EACxC;CACF,CAAC;AACH;;AAGA,eAAsB,kBAAkB,UAAoB,OAAoC;CAC9F,MAAM,OAAO,SAAS,MAAM;CAK5B,MAAM,UAAU,SAAS,QAAQ,aAAa;CAC9C,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,UAAU,cAAc,OAAO;CAE3C,SAAS,QAAQ,SAAS,OAAO,QAAQ;EACvC,IAAI,IAAI,YAAY,MAAM,cACxB;EAEF,MAAM,OAAO,KAAK,KAAK;CACzB,CAAC;CACD,MAAM,KAAK,SAAS,OAAO,MAAM,SAAS,KAAK,IAAI,IAAI;AACzD"}
@@ -0,0 +1,15 @@
1
+ import { AuthUser } from "./http/fastify.js";
2
+ import { AdapterOverrides, BuildContainerOptions, Config, Container, LoggingConfig } from "./app/container.js";
3
+ import { loadIntegrations } from "./app/integrations.js";
4
+ import { ServerConfig } from "./http/config.js";
5
+ import { buildServer } from "./http/server.js";
6
+ import { runMigrations } from "@headless-lms/adapter-db";
7
+ import { InlineAutomationEngine } from "@headless-lms/adapter-defaults/workflows";
8
+ import { AutomationEngine } from "@headless-lms/core/automations";
9
+ import { Mailer } from "@headless-lms/core/shared/mailer";
10
+ import { EmailMessage, EmailSender, ObjectStorage } from "@headless-lms/core/shared/ports";
11
+ //#region src/index.d.ts
12
+ declare function createContainer(config: ServerConfig, options?: BuildContainerOptions): Promise<Container>;
13
+ //#endregion
14
+ export { type AdapterOverrides, type AuthUser, type AutomationEngine, type BuildContainerOptions, type Container, type Config as ContainerConfig, type EmailMessage, type EmailSender, InlineAutomationEngine, type LoggingConfig, type Mailer, type ObjectStorage, type ServerConfig, buildServer, createContainer, loadIntegrations, runMigrations };
15
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { loadIntegrations } from "./app/integrations.js";
2
+ import { buildContainer } from "./app/container.js";
3
+ import { buildServer } from "./http/server.js";
4
+ import { runMigrations } from "@headless-lms/adapter-db";
5
+ import { InlineAutomationEngine } from "@headless-lms/adapter-defaults/workflows";
6
+ //#region src/index.ts
7
+ async function createContainer(config, options) {
8
+ return buildContainer({
9
+ ...config.container,
10
+ deliveryExpirySeconds: config.deliveryExpirySeconds
11
+ }, options);
12
+ }
13
+ //#endregion
14
+ export { InlineAutomationEngine, buildServer, createContainer, loadIntegrations, runMigrations };
15
+
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Public surface of @headless-lms/server. Installations compose these:\n// const container = await createContainer(config, { pluginsDir, adapters })\n// const app = await buildServer(config, container)\nimport {\n buildContainer,\n type BuildContainerOptions,\n type Container,\n} from './app/container.js';\nimport type { ServerConfig } from './http/config.js';\n\nexport { buildServer } from './http/server.js';\nexport { loadIntegrations } from './app/integrations.js';\nexport { InlineAutomationEngine } from '@headless-lms/adapter-defaults/workflows';\n// Operational functions consumed by the @headless-lms/cli bin.\nexport { runMigrations } from '@headless-lms/adapter-db';\nexport type { ServerConfig } from './http/config.js';\n// Re-exporting AuthUser also pulls its module into any program that imports\n// this file (directly or via the workspace path mapping), which is what\n// applies its ambient `declare module \"fastify\"` augmentation\n// (FastifyRequest.authUser/orgId, FastifyInstance.requireOrgSession) —\n// otherwise nothing imports fastify.d.ts and the augmentation never loads.\nexport type { AuthUser } from './http/fastify.js';\nexport type {\n Config as ContainerConfig,\n Container,\n AdapterOverrides,\n BuildContainerOptions,\n LoggingConfig,\n} from './app/container.js';\nexport type { EmailSender, EmailMessage, ObjectStorage } from '@headless-lms/core/shared/ports';\nexport type { Mailer } from '@headless-lms/core/shared/mailer';\nexport type { AutomationEngine } from '@headless-lms/core/automations';\n\nexport async function createContainer(\n config: ServerConfig,\n options?: BuildContainerOptions,\n): Promise<Container> {\n return buildContainer(\n { ...config.container, deliveryExpirySeconds: config.deliveryExpirySeconds },\n options,\n );\n}\n"],"mappings":";;;;;;AAiCA,eAAsB,gBACpB,QACA,SACoB;CACpB,OAAO,eACL;EAAE,GAAG,OAAO;EAAW,uBAAuB,OAAO;CAAsB,GAC3E,OACF;AACF"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@headless-lms/server",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "@fastify/cors": "^11.2.0",
18
+ "@fastify/swagger": "^9.7.0",
19
+ "@fastify/swagger-ui": "^6.0.0",
20
+ "better-auth": "^1.6.25",
21
+ "fastify": "^5.2.0",
22
+ "fastify-type-provider-zod": "^7.0.0",
23
+ "zod": "4.4.3",
24
+ "@headless-lms/adapter-auth": "0.1.0",
25
+ "@headless-lms/adapter-defaults": "0.1.0",
26
+ "@headless-lms/core": "0.1.0",
27
+ "@headless-lms/adapter-db": "0.1.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.10.2",
31
+ "tsdown": "^0.22.9",
32
+ "typescript": "^5.7.2",
33
+ "vitest": "^2.1.8"
34
+ },
35
+ "description": "Fastify server and composition root for the headless LMS",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/mdwt/headless-lms.git",
40
+ "directory": "packages/server"
41
+ },
42
+ "homepage": "https://github.com/mdwt/headless-lms#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/mdwt/headless-lms/issues"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "tsdown",
51
+ "test": "vitest run",
52
+ "lint": "eslint src",
53
+ "format": "prettier --write .",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit"
55
+ }
56
+ }