@headless-lms/server 0.2.0 → 0.3.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/dist/_virtual/_rolldown/runtime.js +5 -0
- package/dist/app/community-mail.d.ts +26 -0
- package/dist/app/community-mail.js +96 -0
- package/dist/app/community-mail.js.map +1 -0
- package/dist/app/container.d.ts +18 -14
- package/dist/app/container.js +136 -43
- package/dist/app/container.js.map +1 -1
- package/dist/app/mcp-endpoints.d.ts +13 -0
- package/dist/app/mcp-endpoints.js +25 -0
- package/dist/app/mcp-endpoints.js.map +1 -0
- package/dist/http/config.d.ts +2 -1
- package/dist/http/errors.d.ts +14 -0
- package/dist/http/errors.js +147 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/mcp/catalogue.d.ts +37 -0
- package/dist/http/mcp/catalogue.js +69 -0
- package/dist/http/mcp/catalogue.js.map +1 -0
- package/dist/http/mcp/challenge.d.ts +7 -0
- package/dist/http/mcp/challenge.js +35 -0
- package/dist/http/mcp/challenge.js.map +1 -0
- package/dist/http/mcp/input-schema.d.ts +7 -0
- package/dist/http/mcp/input-schema.js +28 -0
- package/dist/http/mcp/input-schema.js.map +1 -0
- package/dist/http/mcp/route.d.ts +8 -0
- package/dist/http/mcp/route.js +75 -0
- package/dist/http/mcp/route.js.map +1 -0
- package/dist/http/mcp/server.d.ts +8 -0
- package/dist/http/mcp/server.js +31 -0
- package/dist/http/mcp/server.js.map +1 -0
- package/dist/http/mcp/test-support.d.ts +55 -0
- package/dist/http/mcp/test-support.js +151 -0
- package/dist/http/mcp/test-support.js.map +1 -0
- package/dist/http/mcp/tool.d.ts +48 -0
- package/dist/http/mcp/tool.js +58 -0
- package/dist/http/mcp/tool.js.map +1 -0
- package/dist/http/mcp/tools/automations.d.ts +6 -0
- package/dist/http/mcp/tools/automations.js +120 -0
- package/dist/http/mcp/tools/automations.js.map +1 -0
- package/dist/http/mcp/tools/catalog.d.ts +6 -0
- package/dist/http/mcp/tools/catalog.js +200 -0
- package/dist/http/mcp/tools/catalog.js.map +1 -0
- package/dist/http/mcp/tools/comments.d.ts +6 -0
- package/dist/http/mcp/tools/comments.js +70 -0
- package/dist/http/mcp/tools/comments.js.map +1 -0
- package/dist/http/mcp/tools/communities.d.ts +6 -0
- package/dist/http/mcp/tools/communities.js +175 -0
- package/dist/http/mcp/tools/communities.js.map +1 -0
- package/dist/http/mcp/tools/community-members.d.ts +6 -0
- package/dist/http/mcp/tools/community-members.js +89 -0
- package/dist/http/mcp/tools/community-members.js.map +1 -0
- package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
- package/dist/http/mcp/tools/community-moderation.js +144 -0
- package/dist/http/mcp/tools/community-moderation.js.map +1 -0
- package/dist/http/mcp/tools/courses.d.ts +6 -0
- package/dist/http/mcp/tools/courses.js +121 -0
- package/dist/http/mcp/tools/courses.js.map +1 -0
- package/dist/http/mcp/tools/index.d.ts +6 -0
- package/dist/http/mcp/tools/index.js +23 -0
- package/dist/http/mcp/tools/index.js.map +1 -0
- package/dist/http/mcp/tools/people.d.ts +6 -0
- package/dist/http/mcp/tools/people.js +145 -0
- package/dist/http/mcp/tools/people.js.map +1 -0
- package/dist/http/plugins/auth.js +13 -5
- package/dist/http/plugins/auth.js.map +1 -1
- package/dist/http/plugins/error-handler.d.ts +2 -1
- package/dist/http/plugins/error-handler.js +49 -65
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/plugins/openapi.js +49 -2
- package/dist/http/plugins/openapi.js.map +1 -1
- package/dist/http/routes/access.d.ts +7 -0
- package/dist/http/routes/access.js +140 -0
- package/dist/http/routes/access.js.map +1 -0
- package/dist/http/routes/activities.js +68 -23
- package/dist/http/routes/activities.js.map +1 -1
- package/dist/http/routes/assets.js +60 -55
- package/dist/http/routes/assets.js.map +1 -1
- package/dist/http/routes/automations.js +44 -24
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/bundles.js +29 -18
- package/dist/http/routes/bundles.js.map +1 -1
- package/dist/http/routes/communities.d.ts +7 -0
- package/dist/http/routes/communities.js +462 -0
- package/dist/http/routes/communities.js.map +1 -0
- package/dist/http/routes/community-moderation.d.ts +7 -0
- package/dist/http/routes/community-moderation.js +425 -0
- package/dist/http/routes/community-moderation.js.map +1 -0
- package/dist/http/routes/course-analytics.js +9 -7
- package/dist/http/routes/course-analytics.js.map +1 -1
- package/dist/http/routes/courses.js +30 -15
- package/dist/http/routes/courses.js.map +1 -1
- package/dist/http/routes/dashboard.js +15 -6
- package/dist/http/routes/dashboard.js.map +1 -1
- package/dist/http/routes/discussion.js +34 -26
- package/dist/http/routes/discussion.js.map +1 -1
- package/dist/http/routes/downloads.js +48 -30
- package/dist/http/routes/downloads.js.map +1 -1
- package/dist/http/routes/entitlements.js +16 -7
- package/dist/http/routes/entitlements.js.map +1 -1
- package/dist/http/routes/integrations.js +38 -41
- package/dist/http/routes/integrations.js.map +1 -1
- package/dist/http/routes/member/actor.d.ts +14 -0
- package/dist/http/routes/member/actor.js +11 -0
- package/dist/http/routes/member/actor.js.map +1 -0
- package/dist/http/routes/member/assets.d.ts +7 -0
- package/dist/http/routes/member/assets.js +79 -0
- package/dist/http/routes/member/assets.js.map +1 -0
- package/dist/http/routes/member/chats.d.ts +7 -0
- package/dist/http/routes/member/chats.js +456 -0
- package/dist/http/routes/member/chats.js.map +1 -0
- package/dist/http/routes/member/comments.d.ts +7 -0
- package/dist/http/routes/member/comments.js +187 -0
- package/dist/http/routes/member/comments.js.map +1 -0
- package/dist/http/routes/member/community.d.ts +7 -0
- package/dist/http/routes/member/community.js +288 -0
- package/dist/http/routes/member/community.js.map +1 -0
- package/dist/http/routes/member/courses.d.ts +7 -0
- package/dist/http/routes/member/courses.js +101 -0
- package/dist/http/routes/member/courses.js.map +1 -0
- package/dist/http/routes/member/downloads.d.ts +7 -0
- package/dist/http/routes/member/downloads.js +56 -0
- package/dist/http/routes/member/downloads.js.map +1 -0
- package/dist/http/routes/member/files.d.ts +7 -0
- package/dist/http/routes/member/files.js +184 -0
- package/dist/http/routes/member/files.js.map +1 -0
- package/dist/http/routes/member/index.d.ts +7 -0
- package/dist/http/routes/member/index.js +31 -0
- package/dist/http/routes/member/index.js.map +1 -0
- package/dist/http/routes/member/notifications.d.ts +7 -0
- package/dist/http/routes/member/notifications.js +101 -0
- package/dist/http/routes/member/notifications.js.map +1 -0
- package/dist/http/routes/member/org.d.ts +7 -0
- package/dist/http/routes/member/org.js +56 -0
- package/dist/http/routes/member/org.js.map +1 -0
- package/dist/http/routes/member/posts.d.ts +7 -0
- package/dist/http/routes/member/posts.js +612 -0
- package/dist/http/routes/member/posts.js.map +1 -0
- package/dist/http/routes/member/profile.d.ts +7 -0
- package/dist/http/routes/member/profile.js +48 -0
- package/dist/http/routes/member/profile.js.map +1 -0
- package/dist/http/routes/member/progress.d.ts +7 -0
- package/dist/http/routes/member/progress.js +60 -0
- package/dist/http/routes/member/progress.js.map +1 -0
- package/dist/http/routes/organizations.js +49 -24
- package/dist/http/routes/organizations.js.map +1 -1
- package/dist/http/routes/students.js +32 -47
- package/dist/http/routes/students.js.map +1 -1
- package/dist/http/routes/webhooks.js +26 -18
- package/dist/http/routes/webhooks.js.map +1 -1
- package/dist/http/routes.js +8 -2
- package/dist/http/routes.js.map +1 -1
- package/dist/http/schemas/access.d.ts +146 -0
- package/dist/http/schemas/access.js +36 -0
- package/dist/http/schemas/access.js.map +1 -0
- package/dist/http/schemas/activities.d.ts +3 -1
- package/dist/http/schemas/activities.js +4 -4
- package/dist/http/schemas/activities.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +73 -74
- package/dist/http/schemas/assets.js +23 -13
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +10 -0
- package/dist/http/schemas/bundles.js +6 -6
- package/dist/http/schemas/bundles.js.map +1 -1
- package/dist/http/schemas/chats.d.ts +386 -0
- package/dist/http/schemas/chats.js +58 -0
- package/dist/http/schemas/chats.js.map +1 -0
- package/dist/http/schemas/community.d.ts +537 -0
- package/dist/http/schemas/community.js +129 -0
- package/dist/http/schemas/community.js.map +1 -0
- package/dist/http/schemas/content.d.ts +4 -2
- package/dist/http/schemas/content.js +11 -8
- package/dist/http/schemas/content.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +76 -14
- package/dist/http/schemas/discussion.js +12 -11
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/downloads.d.ts +11 -5
- package/dist/http/schemas/downloads.js +14 -11
- package/dist/http/schemas/downloads.js.map +1 -1
- package/dist/http/schemas/entitlements.d.ts +6 -6
- package/dist/http/schemas/entitlements.js +6 -6
- package/dist/http/schemas/entitlements.js.map +1 -1
- package/dist/http/schemas/files.d.ts +45 -0
- package/dist/http/schemas/files.js +25 -0
- package/dist/http/schemas/files.js.map +1 -0
- package/dist/http/schemas/index.d.ts +15 -8
- package/dist/http/schemas/index.js +15 -8
- package/dist/http/schemas/integrations.d.ts +2 -2
- package/dist/http/schemas/invites.d.ts +4 -0
- package/dist/http/schemas/invites.js +1 -0
- package/dist/http/schemas/invites.js.map +1 -1
- package/dist/http/schemas/{learn.d.ts → member.d.ts} +31 -18
- package/dist/http/schemas/{learn.js → member.js} +17 -13
- package/dist/http/schemas/member.js.map +1 -0
- package/dist/http/schemas/members.d.ts +39 -6
- package/dist/http/schemas/members.js +17 -3
- package/dist/http/schemas/members.js.map +1 -1
- package/dist/http/schemas/notifications.d.ts +227 -0
- package/dist/http/schemas/notifications.js +44 -0
- package/dist/http/schemas/notifications.js.map +1 -0
- package/dist/http/schemas/posts.d.ts +316 -0
- package/dist/http/schemas/posts.js +47 -0
- package/dist/http/schemas/posts.js.map +1 -0
- package/dist/http/schemas/profile.d.ts +23 -0
- package/dist/http/schemas/profile.js +15 -0
- package/dist/http/schemas/profile.js.map +1 -0
- package/dist/http/schemas/shared.d.ts +25 -1
- package/dist/http/schemas/shared.js +19 -2
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +4 -10
- package/dist/http/schemas/students.js +1 -7
- package/dist/http/schemas/students.js.map +1 -1
- package/dist/http/scope.d.ts +2 -1
- package/dist/http/scope.js +20 -4
- package/dist/http/scope.js.map +1 -1
- package/dist/http/server.js +6 -1
- package/dist/http/server.js.map +1 -1
- package/dist/http/web-bridge.d.ts +1 -1
- package/dist/http/web-bridge.js +2 -2
- package/dist/http/web-bridge.js.map +1 -1
- package/dist/http/well-known.d.ts +7 -0
- package/dist/http/well-known.js +22 -0
- package/dist/http/well-known.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
- package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
- package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
- package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
- package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
- package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
- package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
- package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
- package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
- package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
- package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
- package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js +9 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js +51 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js +48 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js +2426 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js.map +1 -0
- package/dist/packages/server/package.js +6 -0
- package/dist/packages/server/package.js.map +1 -0
- package/package.json +9 -7
- package/dist/app/notifications.d.ts +0 -7
- package/dist/app/notifications.js +0 -26
- package/dist/app/notifications.js.map +0 -1
- package/dist/http/routes/learn/assets.d.ts +0 -8
- package/dist/http/routes/learn/assets.js +0 -32
- package/dist/http/routes/learn/assets.js.map +0 -1
- package/dist/http/routes/learn/comments.d.ts +0 -7
- package/dist/http/routes/learn/comments.js +0 -175
- package/dist/http/routes/learn/comments.js.map +0 -1
- package/dist/http/routes/learn/courses.d.ts +0 -7
- package/dist/http/routes/learn/courses.js +0 -98
- package/dist/http/routes/learn/courses.js.map +0 -1
- package/dist/http/routes/learn/downloads.d.ts +0 -7
- package/dist/http/routes/learn/downloads.js +0 -76
- package/dist/http/routes/learn/downloads.js.map +0 -1
- package/dist/http/routes/learn/index.d.ts +0 -7
- package/dist/http/routes/learn/index.js +0 -19
- package/dist/http/routes/learn/index.js.map +0 -1
- package/dist/http/routes/learn/org.d.ts +0 -7
- package/dist/http/routes/learn/org.js +0 -50
- package/dist/http/routes/learn/org.js.map +0 -1
- package/dist/http/routes/learn/progress.d.ts +0 -7
- package/dist/http/routes/learn/progress.js +0 -63
- package/dist/http/routes/learn/progress.js.map +0 -1
- package/dist/http/schemas/learn.js.map +0 -1
- package/dist/package.js +0 -6
- package/dist/package.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
1
2
|
import { resolveScope } from "../scope.js";
|
|
2
|
-
import { ErrorBody } from "../schemas/shared.js";
|
|
3
3
|
import { Entitlement, EntitlementIdParam, EntitlementsPage, EntitlementsQuery, GrantEntitlement, SetEntitlementStatus } from "../schemas/entitlements.js";
|
|
4
4
|
import "../schemas/index.js";
|
|
5
5
|
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
@@ -14,9 +14,13 @@ async function entitlementsRoutes(app, container) {
|
|
|
14
14
|
schema: {
|
|
15
15
|
operationId: "listEntitlements",
|
|
16
16
|
tags: ["Entitlements"],
|
|
17
|
-
summary: "List entitlements",
|
|
17
|
+
summary: "List entitlements (staff)",
|
|
18
|
+
description: "Staff only (owner, admin, instructor).",
|
|
18
19
|
querystring: EntitlementsQuery,
|
|
19
|
-
response: {
|
|
20
|
+
response: {
|
|
21
|
+
200: EntitlementsPage,
|
|
22
|
+
...refusals(403)
|
|
23
|
+
}
|
|
20
24
|
},
|
|
21
25
|
handler: async (req) => {
|
|
22
26
|
const scope = await resolveScope(container, req);
|
|
@@ -30,9 +34,13 @@ async function entitlementsRoutes(app, container) {
|
|
|
30
34
|
schema: {
|
|
31
35
|
operationId: "grantEntitlement",
|
|
32
36
|
tags: ["Entitlements"],
|
|
33
|
-
summary: "Grant a
|
|
37
|
+
summary: "Grant a person access to content or a bundle (staff)",
|
|
38
|
+
description: "Staff only (owner, admin, instructor). Name exactly one of `contentId` and `bundleId`. A bundle grant covers whatever the bundle holds, now and later.",
|
|
34
39
|
body: GrantEntitlement,
|
|
35
|
-
response: {
|
|
40
|
+
response: {
|
|
41
|
+
201: Entitlement,
|
|
42
|
+
...refusals(403, 409)
|
|
43
|
+
}
|
|
36
44
|
},
|
|
37
45
|
handler: async (req, reply) => {
|
|
38
46
|
const scope = await resolveScope(container, req);
|
|
@@ -47,12 +55,13 @@ async function entitlementsRoutes(app, container) {
|
|
|
47
55
|
schema: {
|
|
48
56
|
operationId: "setEntitlementStatus",
|
|
49
57
|
tags: ["Entitlements"],
|
|
50
|
-
summary: "Revoke or reinstate an entitlement",
|
|
58
|
+
summary: "Revoke or reinstate an entitlement (staff)",
|
|
59
|
+
description: "Staff only (owner, admin, instructor). Access follows the status at once.",
|
|
51
60
|
params: EntitlementIdParam,
|
|
52
61
|
body: SetEntitlementStatus,
|
|
53
62
|
response: {
|
|
54
63
|
200: Entitlement,
|
|
55
|
-
404
|
|
64
|
+
...refusals(403, 404)
|
|
56
65
|
}
|
|
57
66
|
},
|
|
58
67
|
handler: async (req) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entitlements.js","names":[],"sources":["../../../src/http/routes/entitlements.ts"],"sourcesContent":["// HTTP routes for the entitlements context (a student's access grant to a piece of content).\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n Entitlement,\n EntitlementIdParam,\n EntitlementsPage,\n EntitlementsQuery,\n
|
|
1
|
+
{"version":3,"file":"entitlements.js","names":[],"sources":["../../../src/http/routes/entitlements.ts"],"sourcesContent":["// HTTP routes for the entitlements context (a student's access grant to a piece of content).\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n Entitlement,\n EntitlementIdParam,\n EntitlementsPage,\n EntitlementsQuery,\n GrantEntitlement,\n SetEntitlementStatus,\n refusals,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function entitlementsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const entitlements = container.entitlements;\n\n r.route({\n method: 'GET',\n url: '/api/entitlements',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listEntitlements',\n tags: ['Entitlements'],\n summary: 'List entitlements (staff)',\n description: 'Staff only (owner, admin, instructor).',\n querystring: EntitlementsQuery,\n response: { 200: EntitlementsPage, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return entitlements.list(scope.orgId, req.query);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/entitlements',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'grantEntitlement',\n tags: ['Entitlements'],\n summary: 'Grant a person access to content or a bundle (staff)',\n description:\n 'Staff only (owner, admin, instructor). Name exactly one of `contentId` and `bundleId`. A bundle grant covers whatever the bundle holds, now and later.',\n body: GrantEntitlement,\n response: { 201: Entitlement, ...refusals(403, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const entitlement = await entitlements.grant(scope.orgId, req.body);\n return reply.code(201).send(entitlement);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/entitlements/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setEntitlementStatus',\n tags: ['Entitlements'],\n summary: 'Revoke or reinstate an entitlement (staff)',\n description: 'Staff only (owner, admin, instructor). Access follows the status at once.',\n params: EntitlementIdParam,\n body: SetEntitlementStatus,\n response: { 200: Entitlement, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const entitlement = await entitlements.setStatus(scope.orgId, req.params.id, req.body.status);\n if (!entitlement) {\n throw new NotFoundError('Entitlement', req.params.id);\n }\n return entitlement;\n },\n });\n}\n"],"mappings":";;;;;;AAgBA,eAAsB,mBACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,eAAe,UAAU;CAE/B,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,cAAc;GACrB,SAAS;GACT,aAAa;GACb,aAAa;GACb,UAAU;IAAE,KAAK;IAAkB,GAAG,SAAS,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,aAAa,KAAK,MAAM,OAAO,IAAI,KAAK;EACjD;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,cAAc;GACrB,SAAS;GACT,aACE;GACF,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,cAAc,MAAM,aAAa,MAAM,MAAM,OAAO,IAAI,IAAI;GAClE,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,WAAW;EACzC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,cAAc;GACrB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,cAAc,MAAM,aAAa,UAAU,MAAM,OAAO,IAAI,OAAO,IAAI,IAAI,KAAK,MAAM;GAC5F,IAAI,CAAC,aACH,MAAM,IAAI,cAAc,eAAe,IAAI,OAAO,EAAE;GAEtD,OAAO;EACT;CACF,CAAC;AACH"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
2
|
+
import { resolveScope } from "../scope.js";
|
|
2
3
|
import { ActionInvocationResult, AvailableIntegrationsList, ConfigureRequest, ConnectRequest, Connection, ConnectionActionParams, ConnectionIdParam, ConnectionsList, InvokeActionRequest, ReconnectRequest } from "../schemas/integrations.js";
|
|
3
4
|
import "../schemas/index.js";
|
|
4
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
6
7
|
//#region src/http/routes/integrations.ts
|
|
7
8
|
function toApi(connection) {
|
|
8
9
|
const { credentialRef: _credentialRef, ...rest } = connection;
|
|
@@ -23,17 +24,21 @@ async function integrationsRoutes(app, container) {
|
|
|
23
24
|
const r = app.withTypeProvider();
|
|
24
25
|
const integrations = container.integrations;
|
|
25
26
|
const tags = ["Integrations"];
|
|
27
|
+
const staff = async (req) => {
|
|
28
|
+
await resolveScope(container, req);
|
|
29
|
+
};
|
|
26
30
|
r.route({
|
|
27
31
|
method: "GET",
|
|
28
32
|
url: "/api/integrations/available",
|
|
29
|
-
preHandler:
|
|
33
|
+
preHandler: staff,
|
|
30
34
|
schema: {
|
|
31
35
|
operationId: "listAvailableIntegrations",
|
|
32
36
|
tags,
|
|
33
|
-
summary: "List the integrations this deployment supports
|
|
37
|
+
summary: "List the integrations this deployment supports (staff)",
|
|
38
|
+
description: "Staff only (owner, admin, instructor). Each with the JSON Schemas of its config, its secrets and its actions.",
|
|
34
39
|
response: {
|
|
35
40
|
200: AvailableIntegrationsList,
|
|
36
|
-
|
|
41
|
+
...refusals(403)
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
handler: async () => integrations.available()
|
|
@@ -41,15 +46,15 @@ async function integrationsRoutes(app, container) {
|
|
|
41
46
|
r.route({
|
|
42
47
|
method: "GET",
|
|
43
48
|
url: "/api/integrations",
|
|
44
|
-
preHandler:
|
|
49
|
+
preHandler: staff,
|
|
45
50
|
schema: {
|
|
46
51
|
operationId: "listConnections",
|
|
47
52
|
tags,
|
|
48
|
-
summary: "List the organization's integration connections",
|
|
53
|
+
summary: "List the organization's integration connections (staff)",
|
|
54
|
+
description: "Staff only (owner, admin, instructor). Secrets are never returned.",
|
|
49
55
|
response: {
|
|
50
56
|
200: ConnectionsList,
|
|
51
|
-
|
|
52
|
-
401: ErrorBody
|
|
57
|
+
...refusals(403)
|
|
53
58
|
}
|
|
54
59
|
},
|
|
55
60
|
handler: async (req, reply) => {
|
|
@@ -61,17 +66,16 @@ async function integrationsRoutes(app, container) {
|
|
|
61
66
|
r.route({
|
|
62
67
|
method: "POST",
|
|
63
68
|
url: "/api/integrations",
|
|
64
|
-
preHandler:
|
|
69
|
+
preHandler: staff,
|
|
65
70
|
schema: {
|
|
66
71
|
operationId: "connectIntegration",
|
|
67
72
|
tags,
|
|
68
|
-
summary: "Connect an integration (
|
|
73
|
+
summary: "Connect an integration (staff)",
|
|
74
|
+
description: "Staff only (owner, admin, instructor). Stores the secrets encrypted; they are never returned. 400 when the integration is unknown or the config is invalid; 409 when the organization already has a connection to it.",
|
|
69
75
|
body: ConnectRequest,
|
|
70
76
|
response: {
|
|
71
77
|
201: Connection,
|
|
72
|
-
400
|
|
73
|
-
401: ErrorBody,
|
|
74
|
-
409: ErrorBody
|
|
78
|
+
...refusals(400, 403, 409)
|
|
75
79
|
}
|
|
76
80
|
},
|
|
77
81
|
handler: async (req, reply) => {
|
|
@@ -84,17 +88,16 @@ async function integrationsRoutes(app, container) {
|
|
|
84
88
|
r.route({
|
|
85
89
|
method: "GET",
|
|
86
90
|
url: "/api/integrations/:id",
|
|
87
|
-
preHandler:
|
|
91
|
+
preHandler: staff,
|
|
88
92
|
schema: {
|
|
89
93
|
operationId: "getConnection",
|
|
90
94
|
tags,
|
|
91
|
-
summary: "Get a connection",
|
|
95
|
+
summary: "Get a connection (staff)",
|
|
96
|
+
description: "Staff only (owner, admin, instructor). Secrets are never returned.",
|
|
92
97
|
params: ConnectionIdParam,
|
|
93
98
|
response: {
|
|
94
99
|
200: Connection,
|
|
95
|
-
|
|
96
|
-
401: ErrorBody,
|
|
97
|
-
404: ErrorBody
|
|
100
|
+
...refusals(403, 404)
|
|
98
101
|
}
|
|
99
102
|
},
|
|
100
103
|
handler: async (req, reply) => {
|
|
@@ -108,18 +111,17 @@ async function integrationsRoutes(app, container) {
|
|
|
108
111
|
r.route({
|
|
109
112
|
method: "PATCH",
|
|
110
113
|
url: "/api/integrations/:id",
|
|
111
|
-
preHandler:
|
|
114
|
+
preHandler: staff,
|
|
112
115
|
schema: {
|
|
113
116
|
operationId: "configureConnection",
|
|
114
117
|
tags,
|
|
115
|
-
summary: "Change a connection's configuration or active flag",
|
|
118
|
+
summary: "Change a connection's configuration or active flag (staff)",
|
|
119
|
+
description: "Staff only (owner, admin, instructor). 400 when the config is invalid for the integration.",
|
|
116
120
|
params: ConnectionIdParam,
|
|
117
121
|
body: ConfigureRequest,
|
|
118
122
|
response: {
|
|
119
123
|
200: Connection,
|
|
120
|
-
400
|
|
121
|
-
401: ErrorBody,
|
|
122
|
-
404: ErrorBody
|
|
124
|
+
...refusals(400, 403, 404)
|
|
123
125
|
}
|
|
124
126
|
},
|
|
125
127
|
handler: async (req, reply) => {
|
|
@@ -133,18 +135,17 @@ async function integrationsRoutes(app, container) {
|
|
|
133
135
|
r.route({
|
|
134
136
|
method: "POST",
|
|
135
137
|
url: "/api/integrations/:id/reconnect",
|
|
136
|
-
preHandler:
|
|
138
|
+
preHandler: staff,
|
|
137
139
|
schema: {
|
|
138
140
|
operationId: "reconnectIntegration",
|
|
139
141
|
tags,
|
|
140
|
-
summary: "Replace a connection's secrets (
|
|
142
|
+
summary: "Replace a connection's secrets (staff)",
|
|
143
|
+
description: "Staff only (owner, admin, instructor). The new secrets replace the stored ones.",
|
|
141
144
|
params: ConnectionIdParam,
|
|
142
145
|
body: ReconnectRequest,
|
|
143
146
|
response: {
|
|
144
147
|
200: Connection,
|
|
145
|
-
|
|
146
|
-
401: ErrorBody,
|
|
147
|
-
404: ErrorBody
|
|
148
|
+
...refusals(403, 404)
|
|
148
149
|
}
|
|
149
150
|
},
|
|
150
151
|
handler: async (req, reply) => {
|
|
@@ -158,20 +159,17 @@ async function integrationsRoutes(app, container) {
|
|
|
158
159
|
r.route({
|
|
159
160
|
method: "POST",
|
|
160
161
|
url: "/api/integrations/:id/actions/:actionId",
|
|
161
|
-
preHandler:
|
|
162
|
+
preHandler: staff,
|
|
162
163
|
schema: {
|
|
163
164
|
operationId: "invokeConnectionAction",
|
|
164
165
|
tags,
|
|
165
|
-
summary: "Invoke one of a connection's actions (
|
|
166
|
+
summary: "Invoke one of a connection's actions (staff)",
|
|
167
|
+
description: "Staff only (owner, admin, instructor). Runs the action, such as a listing that feeds a picker, with `input` checked against its input schema. 400 when the action fails or rejects the input; 404 when the connection or action does not exist; 409 when the connection is inactive.",
|
|
166
168
|
params: ConnectionActionParams,
|
|
167
169
|
body: InvokeActionRequest,
|
|
168
170
|
response: {
|
|
169
171
|
200: ActionInvocationResult,
|
|
170
|
-
400
|
|
171
|
-
401: ErrorBody,
|
|
172
|
-
404: ErrorBody,
|
|
173
|
-
409: ErrorBody,
|
|
174
|
-
502: ErrorBody
|
|
172
|
+
...refusals(400, 403, 404, 409)
|
|
175
173
|
}
|
|
176
174
|
},
|
|
177
175
|
handler: async (req, reply) => {
|
|
@@ -183,17 +181,16 @@ async function integrationsRoutes(app, container) {
|
|
|
183
181
|
r.route({
|
|
184
182
|
method: "DELETE",
|
|
185
183
|
url: "/api/integrations/:id",
|
|
186
|
-
preHandler:
|
|
184
|
+
preHandler: staff,
|
|
187
185
|
schema: {
|
|
188
186
|
operationId: "disconnectIntegration",
|
|
189
187
|
tags,
|
|
190
|
-
summary: "Disconnect an integration (
|
|
188
|
+
summary: "Disconnect an integration (staff)",
|
|
189
|
+
description: "Staff only (owner, admin, instructor). Destroys its stored secrets.",
|
|
191
190
|
params: ConnectionIdParam,
|
|
192
191
|
response: {
|
|
193
192
|
204: z.void(),
|
|
194
|
-
|
|
195
|
-
401: ErrorBody,
|
|
196
|
-
404: ErrorBody
|
|
193
|
+
...refusals(403, 404)
|
|
197
194
|
}
|
|
198
195
|
},
|
|
199
196
|
handler: async (req, reply) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.js","names":[],"sources":["../../../src/http/routes/integrations.ts"],"sourcesContent":["// HTTP routes for integrations (an org's connections to external services).\n// All require a session with an active organization. Credentials are\n// write-only: accepted on connect/reconnect, never present in a response —\n// `toApi` strips the credentialRef so not even the reference leaks.\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActionInvocationResult,\n AvailableIntegrationsList,\n ConfigureRequest,\n ConnectRequest,\n Connection,\n ConnectionActionParams,\n ConnectionIdParam,\n ConnectionsList,\n ErrorBody,\n InvokeActionRequest,\n ReconnectRequest,\n} from '../schemas/index.js';\nimport type { Connection as DomainConnection } from '@headless-lms/core/integrations';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\n\nfunction toApi(connection: DomainConnection): Connection {\n const { credentialRef: _credentialRef, ...rest } = connection;\n return rest;\n}\n\n/** Resolve the session's active org to the domain org id, or 400 and return null. */\nasync function resolveOrgId(req: FastifyRequest, reply: FastifyReply): Promise<string | null> {\n if (!req.orgId) {\n await reply.code(400).send({ error: 'no_active_org', message: 'No active organization' });\n return null;\n }\n return req.orgId;\n}\n\nexport async function integrationsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const integrations = container.integrations;\n const tags = ['Integrations'];\n\n r.route({\n method: 'GET',\n url: '/api/integrations/available',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAvailableIntegrations',\n tags,\n summary: 'List the integrations this deployment supports, with their config schemas',\n response: { 200: AvailableIntegrationsList, 401: ErrorBody },\n },\n handler: async () => integrations.available(),\n });\n\n r.route({\n method: 'GET',\n url: '/api/integrations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listConnections',\n tags,\n summary: \"List the organization's integration connections\",\n response: { 200: ConnectionsList, 400: ErrorBody, 401: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n return (await integrations.list(orgId)).map(toApi);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'connectIntegration',\n tags,\n summary: 'Connect an integration (stores its secrets encrypted)',\n body: ConnectRequest,\n response: { 201: Connection, 400: ErrorBody, 401: ErrorBody, 409: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.connect(orgId, req.body);\n return reply.code(201).send(toApi(connection));\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/integrations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getConnection',\n tags,\n summary: 'Get a connection',\n params: ConnectionIdParam,\n response: { 200: Connection, 400: ErrorBody, 401: ErrorBody, 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.get(orgId, req.params.id);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/integrations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'configureConnection',\n tags,\n summary: \"Change a connection's configuration or active flag\",\n params: ConnectionIdParam,\n body: ConfigureRequest,\n response: { 200: Connection, 400: ErrorBody, 401: ErrorBody, 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.configure(orgId, req.params.id, req.body);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations/:id/reconnect',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reconnectIntegration',\n tags,\n summary: \"Replace a connection's secrets (re-authenticate)\",\n params: ConnectionIdParam,\n body: ReconnectRequest,\n response: { 200: Connection, 400: ErrorBody, 401: ErrorBody, 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.reconnect(orgId, req.params.id, req.body.secrets);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations/:id/actions/:actionId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'invokeConnectionAction',\n tags,\n summary: \"Invoke one of a connection's actions (e.g. a listing that feeds a picker)\",\n params: ConnectionActionParams,\n body: InvokeActionRequest,\n response: {\n 200: ActionInvocationResult,\n 400: ErrorBody,\n 401: ErrorBody,\n 404: ErrorBody,\n 409: ErrorBody,\n 502: ErrorBody,\n },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const output = await integrations.invoke(\n orgId,\n req.params.id,\n req.params.actionId,\n req.body.input ?? {},\n );\n return { output: output as Record<string, unknown> };\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/integrations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'disconnectIntegration',\n tags,\n summary: 'Disconnect an integration (destroys its stored secrets)',\n params: ConnectionIdParam,\n response: { 204: z.void(), 400: ErrorBody, 401: ErrorBody, 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const removed = await integrations.disconnect(orgId, req.params.id);\n if (!removed) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrations.js","names":[],"sources":["../../../src/http/routes/integrations.ts"],"sourcesContent":["// HTTP routes for integrations (an org's connections to external services).\n// All require a session with an active organization. Credentials are\n// write-only: accepted on connect/reconnect, never present in a response —\n// `toApi` strips the credentialRef so not even the reference leaks.\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActionInvocationResult,\n AvailableIntegrationsList,\n ConfigureRequest,\n ConnectRequest,\n Connection,\n ConnectionActionParams,\n ConnectionIdParam,\n ConnectionsList,\n InvokeActionRequest,\n ReconnectRequest,\n refusals,\n} from '../schemas/index.js';\nimport type { Connection as DomainConnection } from '@headless-lms/core/integrations';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nfunction toApi(connection: DomainConnection): Connection {\n const { credentialRef: _credentialRef, ...rest } = connection;\n return rest;\n}\n\n/** Resolve the session's active org to the domain org id, or 400 and return null. */\nasync function resolveOrgId(req: FastifyRequest, reply: FastifyReply): Promise<string | null> {\n if (!req.orgId) {\n await reply.code(400).send({ error: 'no_active_org', message: 'No active organization' });\n return null;\n }\n return req.orgId;\n}\n\nexport async function integrationsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const integrations = container.integrations;\n const tags = ['Integrations'];\n const staff = async (req: FastifyRequest) => {\n await resolveScope(container, req);\n };\n\n r.route({\n method: 'GET',\n url: '/api/integrations/available',\n preHandler: staff,\n schema: {\n operationId: 'listAvailableIntegrations',\n tags,\n summary: 'List the integrations this deployment supports (staff)',\n description:\n 'Staff only (owner, admin, instructor). Each with the JSON Schemas of its config, its secrets and its actions.',\n response: { 200: AvailableIntegrationsList, ...refusals(403) },\n },\n handler: async () => integrations.available(),\n });\n\n r.route({\n method: 'GET',\n url: '/api/integrations',\n preHandler: staff,\n schema: {\n operationId: 'listConnections',\n tags,\n summary: \"List the organization's integration connections (staff)\",\n description: 'Staff only (owner, admin, instructor). Secrets are never returned.',\n response: { 200: ConnectionsList, ...refusals(403) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n return (await integrations.list(orgId)).map(toApi);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations',\n preHandler: staff,\n schema: {\n operationId: 'connectIntegration',\n tags,\n summary: 'Connect an integration (staff)',\n description:\n 'Staff only (owner, admin, instructor). Stores the secrets encrypted; they are never returned. 400 when the integration is unknown or the config is invalid; 409 when the organization already has a connection to it.',\n body: ConnectRequest,\n response: { 201: Connection, ...refusals(400, 403, 409) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.connect(orgId, req.body);\n return reply.code(201).send(toApi(connection));\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/integrations/:id',\n preHandler: staff,\n schema: {\n operationId: 'getConnection',\n tags,\n summary: 'Get a connection (staff)',\n description: 'Staff only (owner, admin, instructor). Secrets are never returned.',\n params: ConnectionIdParam,\n response: { 200: Connection, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.get(orgId, req.params.id);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/integrations/:id',\n preHandler: staff,\n schema: {\n operationId: 'configureConnection',\n tags,\n summary: \"Change a connection's configuration or active flag (staff)\",\n description:\n 'Staff only (owner, admin, instructor). 400 when the config is invalid for the integration.',\n params: ConnectionIdParam,\n body: ConfigureRequest,\n response: { 200: Connection, ...refusals(400, 403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.configure(orgId, req.params.id, req.body);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations/:id/reconnect',\n preHandler: staff,\n schema: {\n operationId: 'reconnectIntegration',\n tags,\n summary: \"Replace a connection's secrets (staff)\",\n description:\n 'Staff only (owner, admin, instructor). The new secrets replace the stored ones.',\n params: ConnectionIdParam,\n body: ReconnectRequest,\n response: { 200: Connection, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const connection = await integrations.reconnect(orgId, req.params.id, req.body.secrets);\n if (!connection) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return toApi(connection);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/integrations/:id/actions/:actionId',\n preHandler: staff,\n schema: {\n operationId: 'invokeConnectionAction',\n tags,\n summary: \"Invoke one of a connection's actions (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Runs the action, such as a listing that feeds a picker, with `input` checked against its input schema. 400 when the action fails or rejects the input; 404 when the connection or action does not exist; 409 when the connection is inactive.',\n params: ConnectionActionParams,\n body: InvokeActionRequest,\n response: { 200: ActionInvocationResult, ...refusals(400, 403, 404, 409) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const output = await integrations.invoke(\n orgId,\n req.params.id,\n req.params.actionId,\n req.body.input ?? {},\n );\n return { output: output as Record<string, unknown> };\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/integrations/:id',\n preHandler: staff,\n schema: {\n operationId: 'disconnectIntegration',\n tags,\n summary: 'Disconnect an integration (staff)',\n description: 'Staff only (owner, admin, instructor). Destroys its stored secrets.',\n params: ConnectionIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const removed = await integrations.disconnect(orgId, req.params.id);\n if (!removed) {\n throw new NotFoundError('Connection', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Container } from "../../../app/container.js";
|
|
2
|
+
//#region src/http/routes/member/actor.d.ts
|
|
3
|
+
declare function actorOf(container: Container, orgId: string, userId: string): Promise<{
|
|
4
|
+
id: string;
|
|
5
|
+
orgId: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
role: "owner" | "admin" | "instructor" | "moderator" | "student";
|
|
8
|
+
status: "invited" | "active";
|
|
9
|
+
createdAt: Date;
|
|
10
|
+
updatedAt: Date;
|
|
11
|
+
}>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { actorOf };
|
|
14
|
+
//# sourceMappingURL=actor.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ForbiddenError } from "@headless-lms/core/shared/errors";
|
|
2
|
+
//#region src/http/routes/member/actor.ts
|
|
3
|
+
async function actorOf(container, orgId, userId) {
|
|
4
|
+
const orgUser = await container.organizations.getOrgUser(orgId, userId);
|
|
5
|
+
if (!orgUser) throw new ForbiddenError("You are not a member of this organization");
|
|
6
|
+
return orgUser;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { actorOf };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=actor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor.js","names":[],"sources":["../../../../src/http/routes/member/actor.ts"],"sourcesContent":["import { ForbiddenError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../../app/container.js';\n\nexport async function actorOf(container: Container, orgId: string, userId: string) {\n const orgUser = await container.organizations.getOrgUser(orgId, userId);\n if (!orgUser) {\n throw new ForbiddenError('You are not a member of this organization');\n }\n return orgUser;\n}\n"],"mappings":";;AAGA,eAAsB,QAAQ,WAAsB,OAAe,QAAgB;CACjF,MAAM,UAAU,MAAM,UAAU,cAAc,WAAW,OAAO,MAAM;CACtE,IAAI,CAAC,SACH,MAAM,IAAI,eAAe,2CAA2C;CAEtE,OAAO;AACT"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Container } from "../../../app/container.js";
|
|
2
|
+
import { FastifyInstance } from "fastify";
|
|
3
|
+
//#region src/http/routes/member/assets.d.ts
|
|
4
|
+
declare function memberAssetsRoutes(app: FastifyInstance, container: Container): Promise<void>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { memberAssetsRoutes };
|
|
7
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { refusals } from "../../schemas/shared.js";
|
|
2
|
+
import { Asset, AssetIdParam, RequestMemberUpload, UploadTicket } from "../../schemas/assets.js";
|
|
3
|
+
import "../../schemas/index.js";
|
|
4
|
+
import { actorOf } from "./actor.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
7
|
+
//#region src/http/routes/member/assets.ts
|
|
8
|
+
const tags = ["Assets"];
|
|
9
|
+
async function memberAssetsRoutes(app, container) {
|
|
10
|
+
const r = app.withTypeProvider();
|
|
11
|
+
r.route({
|
|
12
|
+
method: "POST",
|
|
13
|
+
url: "/api/member/uploads",
|
|
14
|
+
preHandler: app.requireOrgSession,
|
|
15
|
+
schema: {
|
|
16
|
+
operationId: "requestMemberUpload",
|
|
17
|
+
tags,
|
|
18
|
+
summary: "Register an image you are about to upload and get a presigned upload URL",
|
|
19
|
+
description: "For a member of the organization. Member uploads are images only (409 otherwise). The asset is yours: only you can finalize it or attach it to your profile, a post or a message. You can delete it while nothing references it.",
|
|
20
|
+
body: RequestMemberUpload,
|
|
21
|
+
response: {
|
|
22
|
+
201: UploadTicket,
|
|
23
|
+
...refusals(403, 409)
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
handler: async (req, reply) => {
|
|
27
|
+
const actor = await actorOf(container, req.orgId, req.userId);
|
|
28
|
+
const ticket = await container.assets.requestMemberUpload(req.orgId, actor.id, req.body);
|
|
29
|
+
return reply.code(201).send(ticket);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
r.route({
|
|
33
|
+
method: "POST",
|
|
34
|
+
url: "/api/member/uploads/:id/finalize",
|
|
35
|
+
preHandler: app.requireOrgSession,
|
|
36
|
+
schema: {
|
|
37
|
+
operationId: "finalizeMemberUpload",
|
|
38
|
+
tags,
|
|
39
|
+
summary: "Confirm an upload you started",
|
|
40
|
+
description: "Uploader only; anyone else gets 404. Reads the stored size and content type. A file that is not an image or is over the size limit is discarded and answered with 409. A finalized file moves where the upload URL no longer reaches; finalizing it again returns it unchanged.",
|
|
41
|
+
params: AssetIdParam,
|
|
42
|
+
response: {
|
|
43
|
+
200: Asset,
|
|
44
|
+
...refusals(403, 404, 409)
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
handler: async (req) => {
|
|
48
|
+
const actor = await actorOf(container, req.orgId, req.userId);
|
|
49
|
+
const asset = await container.assets.confirmOwn(req.orgId, req.params.id, actor.id);
|
|
50
|
+
if (!asset) throw new NotFoundError("Asset", req.params.id);
|
|
51
|
+
return asset;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
r.route({
|
|
55
|
+
method: "DELETE",
|
|
56
|
+
url: "/api/member/uploads/:id",
|
|
57
|
+
preHandler: app.requireOrgSession,
|
|
58
|
+
schema: {
|
|
59
|
+
operationId: "deleteMemberUpload",
|
|
60
|
+
tags,
|
|
61
|
+
summary: "Delete an upload of yours that nothing references",
|
|
62
|
+
description: "Uploader only; anyone else gets 404. 409 while a profile, post or message still uses it.",
|
|
63
|
+
params: AssetIdParam,
|
|
64
|
+
response: {
|
|
65
|
+
204: z.void(),
|
|
66
|
+
...refusals(403, 404, 409)
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
handler: async (req, reply) => {
|
|
70
|
+
const actor = await actorOf(container, req.orgId, req.userId);
|
|
71
|
+
if (!await container.assets.removeOwn(req.orgId, req.params.id, actor.id)) throw new NotFoundError("Asset", req.params.id);
|
|
72
|
+
return reply.code(204).send();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { memberAssetsRoutes };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","names":[],"sources":["../../../../src/http/routes/member/assets.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n Asset,\n AssetIdParam,\n RequestMemberUpload,\n UploadTicket,\n refusals,\n} from '../../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport { actorOf } from './actor.js';\nimport type { Container } from '../../../app/container.js';\n\nconst tags = ['Assets'];\n\nexport async function memberAssetsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n\n r.route({\n method: 'POST',\n url: '/api/member/uploads',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'requestMemberUpload',\n tags,\n summary: 'Register an image you are about to upload and get a presigned upload URL',\n description:\n 'For a member of the organization. Member uploads are images only (409 otherwise). The asset is yours: only you can finalize it or attach it to your profile, a post or a message. You can delete it while nothing references it.',\n body: RequestMemberUpload,\n response: { 201: UploadTicket, ...refusals(403, 409) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const ticket = await container.assets.requestMemberUpload(req.orgId, actor.id, req.body);\n return reply.code(201).send(ticket);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/uploads/:id/finalize',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'finalizeMemberUpload',\n tags,\n summary: 'Confirm an upload you started',\n description:\n 'Uploader only; anyone else gets 404. Reads the stored size and content type. A file that is not an image or is over the size limit is discarded and answered with 409. A finalized file moves where the upload URL no longer reaches; finalizing it again returns it unchanged.',\n params: AssetIdParam,\n response: { 200: Asset, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const asset = await container.assets.confirmOwn(req.orgId, req.params.id, actor.id);\n if (!asset) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return asset;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/member/uploads/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteMemberUpload',\n tags,\n summary: 'Delete an upload of yours that nothing references',\n description:\n 'Uploader only; anyone else gets 404. 409 while a profile, post or message still uses it.',\n params: AssetIdParam,\n response: { 204: z.void(), ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const removed = await container.assets.removeOwn(req.orgId, req.params.id, actor.id);\n if (!removed) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":";;;;;;;AAcA,MAAM,OAAO,CAAC,QAAQ;AAEtB,eAAsB,mBACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAEhD,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,MAAM;GACN,UAAU;IAAE,KAAK;IAAc,GAAG,SAAS,KAAK,GAAG;GAAE;EACvD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,SAAS,MAAM,UAAU,OAAO,oBAAoB,IAAI,OAAO,MAAM,IAAI,IAAI,IAAI;GACvF,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM;EACpC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAAO,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,QAAQ,MAAM,UAAU,OAAO,WAAW,IAAI,OAAO,IAAI,OAAO,IAAI,MAAM,EAAE;GAClF,IAAI,CAAC,OACH,MAAM,IAAI,cAAc,SAAS,IAAI,OAAO,EAAE;GAEhD,OAAO;EACT;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAE5D,IAAI,CAAC,MADiB,UAAU,OAAO,UAAU,IAAI,OAAO,IAAI,OAAO,IAAI,MAAM,EAAE,GAEjF,MAAM,IAAI,cAAc,SAAS,IAAI,OAAO,EAAE;GAEhD,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Container } from "../../../app/container.js";
|
|
2
|
+
import { FastifyInstance } from "fastify";
|
|
3
|
+
//#region src/http/routes/member/chats.d.ts
|
|
4
|
+
declare function memberChatRoutes(app: FastifyInstance, container: Container): Promise<void>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { memberChatRoutes };
|
|
7
|
+
//# sourceMappingURL=chats.d.ts.map
|