@warlock.js/core 5.2.2 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs.map +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
- package/esm/benchmark/benchmark.d.mts.map +1 -1
- package/esm/benchmark/channels/console.channel.mjs.map +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap/setup.d.mts.map +1 -1
- package/esm/bootstrap/setup.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/start.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/config-setter.d.mts.map +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/access-connector.d.mts.map +1 -1
- package/esm/connectors/ai-connector.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/base-connector.d.mts.map +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.d.mts.map +1 -1
- package/esm/connectors/describe-server-address.mjs.map +1 -1
- package/esm/connectors/herald-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.d.mts.map +1 -1
- package/esm/connectors/mail-connector.d.mts.map +1 -1
- package/esm/connectors/notifications-connector.d.mts.map +1 -1
- package/esm/connectors/register-configured-connectors.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
- package/esm/database/seeds/seeders.manager.mjs.map +1 -1
- package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
- package/esm/dev-server/check-for-updates.mjs.map +1 -1
- package/esm/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.d.mts.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
- package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/source-slug.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs.map +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/ready-block.mjs.map +1 -1
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +28 -11
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +8 -7
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +49 -33
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts.map +1 -1
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts.map +1 -1
- package/esm/http/context/request-memo.mjs.map +1 -1
- package/esm/http/createHttpApplication.d.mts.map +1 -1
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/health.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/ready-report.d.mts.map +1 -1
- package/esm/http/request-controller.d.mts.map +1 -1
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/http/uploads-config.d.mts.map +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/events.d.mts.map +1 -1
- package/esm/mail/mail.d.mts.map +1 -1
- package/esm/mail/mail.mjs.map +1 -1
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/mail/test-mailbox.d.mts.map +1 -1
- package/esm/mail/test-mailbox.mjs.map +1 -1
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-app-production.mjs.map +1 -1
- package/esm/production/dist-build-manifest.mjs.map +1 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/promote-dist.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/register-resource.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/normalize-route-path.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/storage/config.d.mts.map +1 -1
- package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage-file.d.mts.map +1 -1
- package/esm/storage/storage-file.mjs.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/test-connectors-selection.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/updater/package-manager.mjs +15 -1
- package/esm/updater/package-manager.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
- package/esm/use-cases/use-case-events.d.mts.map +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
- package/esm/use-cases/use-case.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.d.mts.map +1 -1
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/get-localized.mjs.map +1 -1
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/normalized-path.d.mts.map +1 -1
- package/esm/utils/paths.d.mts.map +1 -1
- package/esm/utils/promise-all-object.d.mts.map +1 -1
- package/esm/utils/promise-all-object.mjs.map +1 -1
- package/esm/utils/sluggable.mjs.map +1 -1
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
- package/esm/validation/validators/file-validator.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +4 -1
- package/llms.txt +1 -1
- package/package.json +12 -12
- package/skills/README.md +1 -1
- package/skills/write-cli-command/SKILL.md +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restful.mjs","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\nimport type { Model } from \"@warlock.js/cascade\";\nimport { log } from \"@warlock.js/logger\";\nimport type { Request, Response } from \"../http\";\nimport type { QueryBuilderContract, RepositoryManager } from \"../repositories\";\nimport type { HttpContext, RestfulMiddleware, RouteResource } from \"../router\";\n\nexport abstract class Restful<T extends Model> implements RouteResource {\n /**\n * Middleware for each method\n */\n protected middleware: RestfulMiddleware = {};\n\n /**\n * Record name\n */\n protected recordName = \"record\";\n\n /**\n * Records list name\n */\n protected recordsListName = \"records\";\n\n /**\n * Repository\n */\n protected abstract repository: RepositoryManager<T>;\n\n /**\n * Define what to be returned when a record is created|updated|deleted|patched\n */\n protected returnOn: Record<string, \"record\" | \"records\"> = {\n create: \"record\",\n update: \"record\",\n delete: \"record\",\n patch: \"record\",\n };\n\n /**\n * Enable fetching cache\n *\n * @default true\n */\n public cache = true;\n\n /**\n * Find record instance by id\n */\n public async find(id: number) {\n const findMethod = this.cache ? \"getCached\" : \"find\";\n return this.repository[findMethod](id);\n }\n\n /**\n * List records\n */\n public async list({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"list\", request, response)) return;\n\n const responseDocument: GenericObject = {};\n\n const data = request.heavy();\n\n if (data.paginate === \"false\") {\n data.paginate = false;\n }\n\n const listMethod = this.cache ? \"listCached\" : \"list\";\n\n const { data: documents, pagination } = await this.repository[listMethod](data);\n\n responseDocument[this.recordsListName] = documents;\n\n if (pagination) {\n responseDocument.pagination = pagination;\n }\n\n return response.success(responseDocument);\n } catch (error: any) {\n log.error(\"restful\", \"list\", error);\n return response.serverError(error);\n }\n }\n\n /**\n * Get single record\n */\n public async get({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"get\", request, response)) return;\n\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"get\", error);\n }\n }\n\n /**\n * Create a new record\n */\n public async create({ request, response }: HttpContext) {\n try {\n const model = this.repository.newModel();\n const beforeCreate = await this.beforeCreate(request, response, model);\n\n if (beforeCreate) {\n return beforeCreate;\n }\n\n const beforeSave = await this.beforeSave(request, response, model);\n\n if (beforeSave) {\n return beforeSave;\n }\n\n const record = await this.repository.create(request.all());\n\n const createOutput = await this.onCreate(request, response, record);\n\n if (createOutput) {\n return createOutput;\n }\n\n const saveOutput = await this.onSave(request, response, record);\n\n if (saveOutput) {\n return saveOutput;\n }\n\n if (this.returnOn.create === \"records\") {\n return this.list({ request, response });\n }\n\n return response.successCreate({\n [this.recordName]: record,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"create\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Update record\n */\n public async update({ request, response }: HttpContext) {\n try {\n // Find record\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const beforeOutput = await this.beforeUpdate(request, response, record);\n if (beforeOutput) {\n return beforeOutput;\n }\n\n const beforeSafe = await this.beforeSave(request, response, record);\n\n if (beforeSafe) {\n return beforeSafe;\n }\n\n const oldRecord = record.clone();\n\n await record.save(request.allExceptParams());\n\n this.onUpdate(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.update === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"update\", error);\n }\n }\n\n /**\n * Patch record\n */\n public async patch({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const oldRecord = record.clone();\n\n await this.beforePatch(request, response, record, oldRecord);\n await this.beforeSave(request, response, record, oldRecord);\n\n await record.save(request.heavyExceptParams());\n\n this.onPatch(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.patch === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"patch\", error);\n }\n }\n\n /**\n * Delete record\n */\n public async delete({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n if (await this.callMiddleware(\"delete\", request, response, record)) return;\n\n await this.beforeDelete(request, response, record);\n\n await record.destroy();\n\n this.onDelete(request, response, record);\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success();\n } catch (error: Error | any) {\n log.error(\"restful\", \"delete\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Bulk delete records\n */\n public async bulkDelete({ request, response }: HttpContext) {\n try {\n const ids = request.input(\"id\");\n\n if (!Array.isArray(ids)) {\n return response.badRequest({\n error: \"id must be an array\",\n });\n }\n\n const records = await this.repository.all({\n perform: (query: QueryBuilderContract<T>) =>\n query.whereIn(\n \"id\",\n ids.map((id) => parseInt(id)),\n ),\n });\n\n await Promise.all(\n records.map(async (record) => {\n if (await this.callMiddleware(\"delete\", request, response, record)) {\n return;\n }\n\n await this.beforeDelete(request, response, record);\n await record.destroy();\n this.onDelete(request, response, record);\n }),\n );\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n deleted: records.length,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"bulkDelete\", error);\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Before create\n */\n protected async beforeCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On create\n */\n protected async onCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before update\n */\n protected async beforeUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On update\n */\n protected async onUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before delete\n */\n protected async beforeDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On delete\n */\n protected async onDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before patch\n */\n protected async beforePatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On patch\n */\n protected async onPatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before save\n */\n protected async beforeSave(\n _request: Request,\n _response: Response,\n _record?: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On save\n */\n protected async onSave(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Call middleware for the given method\n *\n */\n protected async callMiddleware(\n method: string,\n request: Request,\n response: Response,\n _record?: any,\n ) {\n if (!this.middleware[method]) return;\n\n for (const middleware of this.middleware[method]) {\n const output = await middleware({ request, response });\n\n if (output) {\n return output;\n }\n }\n\n return;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"restful.mjs","names":[],"sources":["../../../../../../../core/src/restful/restful.ts"],"sourcesContent":["import type { GenericObject } from \"@mongez/reinforcements\";\nimport type { Model } from \"@warlock.js/cascade\";\nimport { log } from \"@warlock.js/logger\";\nimport type { Request, Response } from \"../http\";\nimport type { QueryBuilderContract, RepositoryManager } from \"../repositories\";\nimport type { HttpContext, RestfulMiddleware, RouteResource } from \"../router\";\n\nexport abstract class Restful<T extends Model> implements RouteResource {\n /**\n * Middleware for each method\n */\n protected middleware: RestfulMiddleware = {};\n\n /**\n * Record name\n */\n protected recordName = \"record\";\n\n /**\n * Records list name\n */\n protected recordsListName = \"records\";\n\n /**\n * Repository\n */\n protected abstract repository: RepositoryManager<T>;\n\n /**\n * Define what to be returned when a record is created|updated|deleted|patched\n */\n protected returnOn: Record<string, \"record\" | \"records\"> = {\n create: \"record\",\n update: \"record\",\n delete: \"record\",\n patch: \"record\",\n };\n\n /**\n * Enable fetching cache\n *\n * @default true\n */\n public cache = true;\n\n /**\n * Find record instance by id\n */\n public async find(id: number) {\n const findMethod = this.cache ? \"getCached\" : \"find\";\n return this.repository[findMethod](id);\n }\n\n /**\n * List records\n */\n public async list({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"list\", request, response)) return;\n\n const responseDocument: GenericObject = {};\n\n const data = request.heavy();\n\n if (data.paginate === \"false\") {\n data.paginate = false;\n }\n\n const listMethod = this.cache ? \"listCached\" : \"list\";\n\n const { data: documents, pagination } = await this.repository[listMethod](data);\n\n responseDocument[this.recordsListName] = documents;\n\n if (pagination) {\n responseDocument.pagination = pagination;\n }\n\n return response.success(responseDocument);\n } catch (error: any) {\n log.error(\"restful\", \"list\", error);\n return response.serverError(error);\n }\n }\n\n /**\n * Get single record\n */\n public async get({ request, response }: HttpContext) {\n try {\n if (await this.callMiddleware(\"get\", request, response)) return;\n\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"get\", error);\n }\n }\n\n /**\n * Create a new record\n */\n public async create({ request, response }: HttpContext) {\n try {\n const model = this.repository.newModel();\n const beforeCreate = await this.beforeCreate(request, response, model);\n\n if (beforeCreate) {\n return beforeCreate;\n }\n\n const beforeSave = await this.beforeSave(request, response, model);\n\n if (beforeSave) {\n return beforeSave;\n }\n\n const record = await this.repository.create(request.all());\n\n const createOutput = await this.onCreate(request, response, record);\n\n if (createOutput) {\n return createOutput;\n }\n\n const saveOutput = await this.onSave(request, response, record);\n\n if (saveOutput) {\n return saveOutput;\n }\n\n if (this.returnOn.create === \"records\") {\n return this.list({ request, response });\n }\n\n return response.successCreate({\n [this.recordName]: record,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"create\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Update record\n */\n public async update({ request, response }: HttpContext) {\n try {\n // Find record\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const beforeOutput = await this.beforeUpdate(request, response, record);\n if (beforeOutput) {\n return beforeOutput;\n }\n\n const beforeSafe = await this.beforeSave(request, response, record);\n\n if (beforeSafe) {\n return beforeSafe;\n }\n\n const oldRecord = record.clone();\n\n await record.save(request.allExceptParams());\n\n this.onUpdate(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.update === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"update\", error);\n }\n }\n\n /**\n * Patch record\n */\n public async patch({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound({\n error: \"Record not found\",\n });\n }\n\n const oldRecord = record.clone();\n\n await this.beforePatch(request, response, record, oldRecord);\n await this.beforeSave(request, response, record, oldRecord);\n\n await record.save(request.heavyExceptParams());\n\n this.onPatch(request, response, record, oldRecord);\n this.onSave(request, response, record, oldRecord);\n\n if (this.returnOn.patch === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n [this.recordName]: record,\n });\n } catch (error) {\n log.error(\"restful\", \"patch\", error);\n }\n }\n\n /**\n * Delete record\n */\n public async delete({ request, response }: HttpContext) {\n try {\n const record = await this.find(request.input(\"id\"));\n\n if (!record) {\n return response.notFound();\n }\n\n if (await this.callMiddleware(\"delete\", request, response, record)) return;\n\n await this.beforeDelete(request, response, record);\n\n await record.destroy();\n\n this.onDelete(request, response, record);\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success();\n } catch (error: Error | any) {\n log.error(\"restful\", \"delete\", error);\n\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Bulk delete records\n */\n public async bulkDelete({ request, response }: HttpContext) {\n try {\n const ids = request.input(\"id\");\n\n if (!Array.isArray(ids)) {\n return response.badRequest({\n error: \"id must be an array\",\n });\n }\n\n const records = await this.repository.all({\n perform: (query: QueryBuilderContract<T>) =>\n query.whereIn(\n \"id\",\n ids.map((id) => parseInt(id)),\n ),\n });\n\n await Promise.all(\n records.map(async (record) => {\n if (await this.callMiddleware(\"delete\", request, response, record)) {\n return;\n }\n\n await this.beforeDelete(request, response, record);\n await record.destroy();\n this.onDelete(request, response, record);\n }),\n );\n\n if (this.returnOn.delete === \"records\") {\n return this.list({ request, response });\n }\n\n return response.success({\n deleted: records.length,\n });\n } catch (error: Error | any) {\n log.error(\"restful\", \"bulkDelete\", error);\n return response.badRequest({\n error: error.message,\n });\n }\n }\n\n /**\n * Before create\n */\n protected async beforeCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On create\n */\n protected async onCreate(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before update\n */\n protected async beforeUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On update\n */\n protected async onUpdate(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before delete\n */\n protected async beforeDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * On delete\n */\n protected async onDelete(_request: Request, _response: Response, _record: T): Promise<any> {\n //\n }\n\n /**\n * Before patch\n */\n protected async beforePatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On patch\n */\n protected async onPatch(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Before save\n */\n protected async beforeSave(\n _request: Request,\n _response: Response,\n _record?: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * On save\n */\n protected async onSave(\n _request: Request,\n _response: Response,\n _record: T,\n _oldRecord?: T,\n ): Promise<any> {\n //\n }\n\n /**\n * Call middleware for the given method\n *\n */\n protected async callMiddleware(\n method: string,\n request: Request,\n response: Response,\n _record?: any,\n ) {\n if (!this.middleware[method]) return;\n\n for (const middleware of this.middleware[method]) {\n const output = await middleware({ request, response });\n\n if (output) {\n return output;\n }\n }\n\n return;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-request-lifecycle.mjs","names":[],"sources":["../../../../../../../core/src/router/log-request-lifecycle.ts"],"sourcesContent":["/**\n * Both ends of a request's log entry.\n *\n * ## Why the completion line exists\n *\n * The router logged `Starting Request: <id>` and nothing else. A request that\n * hung for thirty seconds and one that returned in two milliseconds produced\n * **byte-identical output**, so the log could not answer the one question\n * anyone opens it to ask.\n *\n * Note what \"detecting a hang\" actually means. Nothing can log the completion\n * of a request that never completes — the mechanism is the **absence** of a\n * completion line beside a start line that has one. That only works if the\n * completion line is emitted on every settled path, **including a throw**,\n * which is why the error path here is not an afterthought.\n *\n * ## Why the level tracks the status\n *\n * A handled 500 does not throw; it returns a response. Logging it at `info`\n * would bury the exact entries someone scanning the log is hunting for, so the\n * level is derived from the outcome: informational below 400, a warning for\n * client errors, an error for server errors and for anything that threw.\n *\n * ## Ports rather than a direct `log` import\n *\n * The behaviour worth pinning down is *which lines come out, in which order,\n * at which level, when the run throws*. Injecting the sink and the clock makes\n * that assertable without a server, a socket or a fake timer.\n */\n\nexport type RequestLogEntry = {\n module: string;\n action: string;\n message: string;\n /**\n * Typed as the logger accepts it, not as `unknown`. These entries exist to be\n * handed to `log.info` / `warn` / `error`, whose parameter is\n * `Record<string, any>` — `unknown` was not assignable, so the adapter in\n * router.ts could not compile even though every real caller passes an object.\n */\n context?: Record<string, any>;\n};\n\nexport type RequestLogPorts = {\n info(entry: RequestLogEntry): void;\n warn(entry: RequestLogEntry): void;\n error(entry: RequestLogEntry): void;\n /** Monotonic milliseconds. Called exactly twice per request. */\n now(): number;\n};\n\nexport type RequestLogDescriptor = {\n module: string;\n action: string;\n requestId: string;\n /**\n * Typed as the logger accepts it, not as `unknown`. These entries exist to be\n * handed to `log.info` / `warn` / `error`, whose parameter is\n * `Record<string, any>` — `unknown` was not assignable, so the adapter in\n * router.ts could not compile even though every real caller passes an object.\n */\n context?: Record<string, any>;\n /**\n * Read AFTER the run settles — the status is decided during the request, so\n * capturing it up front would report the default on every entry.\n */\n statusCode(): number | undefined;\n};\n\ntype Level = \"info\" | \"warn\" | \"error\";\n\nfunction levelForStatus(statusCode: number | undefined): Level {\n // An unknown status is not evidence of a problem; saying \"error\" because we\n // failed to read a number would be inventing a defect.\n if (statusCode === undefined) return \"info\";\n\n if (statusCode >= 500) return \"error\";\n\n if (statusCode >= 400) return \"warn\";\n\n return \"info\";\n}\n\nexport async function logRequestLifecycle<TResult>(\n ports: RequestLogPorts,\n descriptor: RequestLogDescriptor,\n run: () => Promise<TResult>,\n): Promise<TResult> {\n const startedAt = ports.now();\n\n ports.info({\n module: descriptor.module,\n action: descriptor.action,\n message: `Starting Request: ${descriptor.requestId}`,\n context: descriptor.context,\n });\n\n const finish = (level: Level, outcome: string): void => {\n const duration = Math.round(ports.now() - startedAt);\n\n ports[level]({\n module: descriptor.module,\n action: descriptor.action,\n message: `Completed Request: ${descriptor.requestId} — ${outcome} in ${duration}ms`,\n context: descriptor.context,\n });\n };\n\n try {\n const result = await run();\n\n const statusCode = descriptor.statusCode();\n\n finish(levelForStatus(statusCode), statusCode === undefined ? \"no status\" : String(statusCode));\n\n return result;\n } catch (error) {\n // The throw is reported and then re-thrown untouched: this function\n // observes the request, it does not own its error handling.\n finish(\"error\", `threw ${error instanceof Error ? error.name : typeof error}`);\n\n throw error;\n }\n}\n"],"mappings":";AAuEA,SAAS,eAAe,YAAuC;CAG7D,IAAI,eAAe,QAAW,OAAO;CAErC,IAAI,cAAc,KAAK,OAAO;CAE9B,IAAI,cAAc,KAAK,OAAO;CAE9B,OAAO;AACT;AAEA,eAAsB,oBACpB,OACA,YACA,KACkB;CAClB,MAAM,YAAY,MAAM,IAAI;CAE5B,MAAM,KAAK;EACT,QAAQ,WAAW;EACnB,QAAQ,WAAW;EACnB,SAAS,qBAAqB,WAAW;EACzC,SAAS,WAAW;CACtB,CAAC;CAED,MAAM,UAAU,OAAc,YAA0B;EACtD,MAAM,WAAW,KAAK,MAAM,MAAM,IAAI,IAAI,SAAS;EAEnD,MAAM,
|
|
1
|
+
{"version":3,"file":"log-request-lifecycle.mjs","names":[],"sources":["../../../../../../../core/src/router/log-request-lifecycle.ts"],"sourcesContent":["/**\n * Both ends of a request's log entry.\n *\n * ## Why the completion line exists\n *\n * The router logged `Starting Request: <id>` and nothing else. A request that\n * hung for thirty seconds and one that returned in two milliseconds produced\n * **byte-identical output**, so the log could not answer the one question\n * anyone opens it to ask.\n *\n * Note what \"detecting a hang\" actually means. Nothing can log the completion\n * of a request that never completes — the mechanism is the **absence** of a\n * completion line beside a start line that has one. That only works if the\n * completion line is emitted on every settled path, **including a throw**,\n * which is why the error path here is not an afterthought.\n *\n * ## Why the level tracks the status\n *\n * A handled 500 does not throw; it returns a response. Logging it at `info`\n * would bury the exact entries someone scanning the log is hunting for, so the\n * level is derived from the outcome: informational below 400, a warning for\n * client errors, an error for server errors and for anything that threw.\n *\n * ## Ports rather than a direct `log` import\n *\n * The behaviour worth pinning down is *which lines come out, in which order,\n * at which level, when the run throws*. Injecting the sink and the clock makes\n * that assertable without a server, a socket or a fake timer.\n */\n\nexport type RequestLogEntry = {\n module: string;\n action: string;\n message: string;\n /**\n * Typed as the logger accepts it, not as `unknown`. These entries exist to be\n * handed to `log.info` / `warn` / `error`, whose parameter is\n * `Record<string, any>` — `unknown` was not assignable, so the adapter in\n * router.ts could not compile even though every real caller passes an object.\n */\n context?: Record<string, any>;\n};\n\nexport type RequestLogPorts = {\n info(entry: RequestLogEntry): void;\n warn(entry: RequestLogEntry): void;\n error(entry: RequestLogEntry): void;\n /** Monotonic milliseconds. Called exactly twice per request. */\n now(): number;\n};\n\nexport type RequestLogDescriptor = {\n module: string;\n action: string;\n requestId: string;\n /**\n * Typed as the logger accepts it, not as `unknown`. These entries exist to be\n * handed to `log.info` / `warn` / `error`, whose parameter is\n * `Record<string, any>` — `unknown` was not assignable, so the adapter in\n * router.ts could not compile even though every real caller passes an object.\n */\n context?: Record<string, any>;\n /**\n * Read AFTER the run settles — the status is decided during the request, so\n * capturing it up front would report the default on every entry.\n */\n statusCode(): number | undefined;\n};\n\ntype Level = \"info\" | \"warn\" | \"error\";\n\nfunction levelForStatus(statusCode: number | undefined): Level {\n // An unknown status is not evidence of a problem; saying \"error\" because we\n // failed to read a number would be inventing a defect.\n if (statusCode === undefined) return \"info\";\n\n if (statusCode >= 500) return \"error\";\n\n if (statusCode >= 400) return \"warn\";\n\n return \"info\";\n}\n\nexport async function logRequestLifecycle<TResult>(\n ports: RequestLogPorts,\n descriptor: RequestLogDescriptor,\n run: () => Promise<TResult>,\n): Promise<TResult> {\n const startedAt = ports.now();\n\n ports.info({\n module: descriptor.module,\n action: descriptor.action,\n message: `Starting Request: ${descriptor.requestId}`,\n context: descriptor.context,\n });\n\n const finish = (level: Level, outcome: string): void => {\n const duration = Math.round(ports.now() - startedAt);\n\n ports[level]({\n module: descriptor.module,\n action: descriptor.action,\n message: `Completed Request: ${descriptor.requestId} — ${outcome} in ${duration}ms`,\n context: descriptor.context,\n });\n };\n\n try {\n const result = await run();\n\n const statusCode = descriptor.statusCode();\n\n finish(levelForStatus(statusCode), statusCode === undefined ? \"no status\" : String(statusCode));\n\n return result;\n } catch (error) {\n // The throw is reported and then re-thrown untouched: this function\n // observes the request, it does not own its error handling.\n finish(\"error\", `threw ${error instanceof Error ? error.name : typeof error}`);\n\n throw error;\n }\n}\n"],"mappings":";AAuEA,SAAS,eAAe,YAAuC;CAG7D,IAAI,eAAe,QAAW,OAAO;CAErC,IAAI,cAAc,KAAK,OAAO;CAE9B,IAAI,cAAc,KAAK,OAAO;CAE9B,OAAO;AACT;AAEA,eAAsB,oBACpB,OACA,YACA,KACkB;CAClB,MAAM,YAAY,MAAM,IAAI;CAE5B,MAAM,KAAK;EACT,QAAQ,WAAW;EACnB,QAAQ,WAAW;EACnB,SAAS,qBAAqB,WAAW;EACzC,SAAS,WAAW;CACtB,CAAC;CAED,MAAM,UAAU,OAAc,YAA0B;EACtD,MAAM,WAAW,KAAK,MAAM,MAAM,IAAI,IAAI,SAAS;EAEnD,MAAM,MAAM,CAAC;GACX,QAAQ,WAAW;GACnB,QAAQ,WAAW;GACnB,SAAS,sBAAsB,WAAW,UAAU,KAAK,QAAQ,MAAM,SAAS;GAChF,SAAS,WAAW;EACtB,CAAC;CACH;CAEA,IAAI;EACF,MAAM,SAAS,MAAM,IAAI;EAEzB,MAAM,aAAa,WAAW,WAAW;EAEzC,OAAO,eAAe,UAAU,GAAG,eAAe,SAAY,cAAc,OAAO,UAAU,CAAC;EAE9F,OAAO;CACT,SAAS,OAAO;EAGd,OAAO,SAAS,SAAS,iBAAiB,QAAQ,MAAM,OAAO,OAAO,OAAO;EAE7E,MAAM;CACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-route-path.d.mts","names":[],"sources":["../../../../../../../core/src/router/normalize-route-path.ts"],"mappings":";;AAyBA;;;;AAA8C;;;;;;;;;;;;;;;;;;iBAA9B,kBAAA,
|
|
1
|
+
{"version":3,"file":"normalize-route-path.d.mts","names":[],"sources":["../../../../../../../core/src/router/normalize-route-path.ts"],"mappings":";;AAyBA;;;;AAA8C;;;;;;;;;;;;;;;;;;iBAA9B,kBAAA,IAAsB,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positional-handler-diagnostics.d.mts","names":[],"sources":["../../../../../../../core/src/router/positional-handler-diagnostics.ts"],"mappings":";;;;AAyPsE;AAmBtE;;AAnBsE,KArN1D,wBAAA;EAwOsE,kDAtOhF,MAAA,EAAQ,KAAK,YA+OC;EA5Od,IAAA;EAGA,WAAA,UAyOgE;EAtOhE,UAAA;AAAA;;;AAkP4C;AAS9C;;;;AAAkF;AAclF;;;;;;;;;;;;;AAAsF;;;;;;;;;;;iBAhGtE,0BAAA,CAA2B,OAAgB;;;;;;;;;iBA+B3C,uBAAA,CACd,OAAA,WACA,KAAA;EAAS,MAAA,EAAQ,KAAK;EAAY,IAAA;EAAc,UAAA;AAAA;;;;iBAmBlC,6BAAA,
|
|
1
|
+
{"version":3,"file":"positional-handler-diagnostics.d.mts","names":[],"sources":["../../../../../../../core/src/router/positional-handler-diagnostics.ts"],"mappings":";;;;AAyPsE;AAmBtE;;AAnBsE,KArN1D,wBAAA;EAwOsE,kDAtOhF,MAAA,EAAQ,KAAK,YA+OC;EA5Od,IAAA;EAGA,WAAA,UAyOgE;EAtOhE,UAAA;AAAA;;;AAkP4C;AAS9C;;;;AAAkF;AAclF;;;;;;;;;;;;;AAAsF;;;;;;;;;;;iBAhGtE,0BAAA,CAA2B,OAAgB;;;;;;;;;iBA+B3C,uBAAA,CACd,OAAA,WACA,KAAA;EAAS,MAAA,EAAQ,KAAK;EAAY,IAAA;EAAc,UAAA;AAAA;;;;iBAmBlC,6BAAA,aAA0C,wBAAwB;;;;;;iBASlE,+BAAA,CAAgC,UAAkB;;;;;iBAYlD,8BAAA;;;;;;iBASA,gCAAA,CAAiC,OAAiC,EAAxB,wBAAwB;;;;;;;iBAclE,+BAAA,CAAgC,MAAA,GAAQ,IAAA,QAAY,GAAA,YAAkB,wBAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positional-handler-diagnostics.mjs","names":[],"sources":["../../../../../../../core/src/router/positional-handler-diagnostics.ts"],"sourcesContent":["/**\n * Early detection of route handlers still written against the **v4 positional\n * signature** — `(request, response)` — after v5 moved to a single context\n * object, `({ request, response })`.\n *\n * ## Why this exists\n *\n * A v4 handler is not rejected anywhere. It registers fine, and the router\n * calls it with one argument, so `response` is simply `undefined`. The app only\n * breaks on the first request that reaches it, with:\n *\n * TypeError: Cannot read properties of undefined (reading 'success')\n *\n * That message names nothing the reader can act on — not the route, not the\n * handler, not the change to make. This module turns that late, opaque failure\n * into one explicit list printed at boot, and into a `warlock doctor` check.\n *\n * The break itself is intentional; nothing here restores the positional form.\n * Detection and diagnostics only.\n *\n * ## Why it warns and never throws\n *\n * The signal is a heuristic (see {@link looksLikePositionalHandler}), so a\n * false positive is possible in principle. Turning one into a boot failure\n * would take a working application down over a guess, which is strictly worse\n * than the problem being diagnosed. Everything on this path is therefore a\n * warning: nothing here throws, and nothing here changes how a handler is\n * called. A miss simply degrades to the old runtime `TypeError`.\n */\n\nimport { log } from \"@warlock.js/logger\";\nimport type { Route } from \"./types\";\n\n/**\n * One route whose handler looks like it was written for v4.\n */\nexport type PositionalHandlerSuspect = {\n /** HTTP method the route was registered under. */\n method: Route[\"method\"];\n\n /** Full route path, prefixes already applied. */\n path: string;\n\n /** Handler function name, or `\"(anonymous)\"` when it has none. */\n handlerName: string;\n\n /** Route file the route came from, when the router knows it. */\n sourceFile: string;\n};\n\n/**\n * Collected across the whole registration pass so the warning can be emitted\n * ONCE, as a single list, rather than one line per route (or — as before — one\n * mystery 500 per request).\n */\nconst suspects: PositionalHandlerSuspect[] = [];\n\n/**\n * Split the parameter list of a function's source into top-level parameters.\n *\n * Depth-aware and quote-aware, because a default value may itself contain\n * commas, parentheses, braces or strings — `(request, response = f(a, b))` is\n * two parameters, not three. Anything unbalanced returns `undefined`: refusing\n * to guess is what keeps a parse quirk from becoming a false warning.\n *\n * @param openIndex index of the `(` that opens the parameter list.\n */\nfunction splitTopLevelParameters(source: string, openIndex: number): string[] | undefined {\n const parameters: string[] = [];\n\n let depth = 0;\n let current = \"\";\n let quote = \"\";\n\n for (let index = openIndex; index < source.length; index++) {\n const char = source[index];\n\n if (quote) {\n if (char === \"\\\\\") {\n current += char + (source[index + 1] ?? \"\");\n index++;\n continue;\n }\n\n if (char === quote) {\n quote = \"\";\n }\n\n current += char;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n quote = char;\n current += char;\n continue;\n }\n\n if (char === \"(\" || char === \"[\" || char === \"{\") {\n depth++;\n\n // The paren that opens the list is punctuation, not part of a parameter.\n if (depth === 1) continue;\n\n current += char;\n continue;\n }\n\n if (char === \")\" || char === \"]\" || char === \"}\") {\n depth--;\n\n if (depth === 0) {\n if (current.trim()) {\n parameters.push(current.trim());\n }\n\n return parameters;\n }\n\n current += char;\n continue;\n }\n\n if (char === \",\" && depth === 1) {\n parameters.push(current.trim());\n current = \"\";\n continue;\n }\n\n current += char;\n }\n\n // Never found the closing paren — treat the source as unreadable.\n return undefined;\n}\n\n/**\n * Read a function's declared parameters from its own source.\n *\n * Returns `undefined` whenever the source cannot be trusted, which callers must\n * treat as \"no information\" rather than \"no parameters\":\n *\n * - `Function.prototype.bind` replaces the body with `[native code]`, so every\n * `[Controller, \"method\"]` tuple handler lands here (the router binds them);\n * - native functions likewise;\n * - a class, whose first `(` belongs to a constructor or a method, not to a\n * parameter list;\n * - anything that fails to parse.\n */\nfunction readParameterList(handler: Function): string[] | undefined {\n let source: string;\n\n try {\n source = Function.prototype.toString.call(handler);\n } catch {\n return undefined;\n }\n\n if (source.includes(\"[native code]\")) return undefined;\n\n if (/^\\s*class[\\s{]/.test(source)) return undefined;\n\n // A single parameter needs no parentheses: `ctx => …`, `async ctx => …`.\n const bareArrow = source.match(/^\\s*(?:async\\s+)?([A-Za-z_$][\\w$]*)\\s*=>/);\n\n if (bareArrow) return [bareArrow[1]];\n\n const openIndex = source.indexOf(\"(\");\n\n if (openIndex === -1) return undefined;\n\n return splitTopLevelParameters(source, openIndex);\n}\n\n/**\n * Whether a parameter is written as a destructuring pattern — `{ request }` or\n * `[a, b]` — as opposed to a plain name.\n */\nfunction isDestructured(parameter: string) {\n const start = parameter.trimStart();\n\n return start.startsWith(\"{\") || start.startsWith(\"[\");\n}\n\n/**\n * Does this handler look like it was written for the v4 positional signature?\n *\n * ## The signal, and what it misses\n *\n * `Function.length` alone is not enough, because it stops counting at the first\n * defaulted parameter:\n *\n * | handler | `.length` | actually |\n * | ---------------------------------- | --------- | -------- |\n * | `({ request, response }) => …` | 1 | v5 |\n * | `(ctx) => …` | 1 | v5 |\n * | `(request, response) => …` | 2 | v4 |\n * | `(request, response = x) => …` | 1 | v4 |\n *\n * So the primary signal is the function's own source: a handler is suspect when\n * it declares **two or more** parameters and the first is **not** destructured.\n * Reading the source catches the defaulted row that arity cannot, and rules out\n * the `({ … }, extra)` shape that arity would wrongly flag.\n *\n * When the source is unreadable — a bound or native function — this falls back\n * to `handler.length >= 2`, which `bind` preserves. That is a weaker signal but\n * never a wrong one for the common cases; it only loses the defaulted row.\n *\n * ## What is still missed, by design\n *\n * A one-parameter v4 handler (`(request) => …`) is indistinguishable from a v5\n * `(ctx) => …` and is deliberately not flagged — guessing there would produce\n * false positives on correct code. False positives against the canonical\n * `({ … })` form are essentially nil, and every miss simply leaves today's\n * behaviour in place.\n */\nexport function looksLikePositionalHandler(handler: unknown): boolean {\n if (typeof handler !== \"function\") return false;\n\n const parameters = readParameterList(handler);\n\n if (parameters) {\n return parameters.length >= 2 && !isDestructured(parameters[0]);\n }\n\n return handler.length >= 2;\n}\n\n/**\n * The name to print for a handler.\n *\n * `Function.bind` prefixes the name with `\"bound \"`, and the router binds every\n * `[Controller, \"method\"]` tuple handler. The bare method name is what the user\n * actually wrote, so that is what gets printed.\n */\nfunction handlerDisplayName(handler: Function): string {\n return handler.name?.replace(/^bound /, \"\").trim() || \"(anonymous)\";\n}\n\n/**\n * Inspect one route's handler as it registers, recording it when it looks like\n * the v4 positional form. Called from `Router.add`, so the cost is paid once per\n * route at registration and never per request.\n *\n * Deliberately total: any unexpected failure in the heuristic is swallowed, so a\n * diagnostic can never be the reason a route fails to register.\n */\nexport function inspectHandlerSignature(\n handler: unknown,\n route: { method: Route[\"method\"]; path: string; sourceFile: string },\n) {\n try {\n if (!looksLikePositionalHandler(handler)) return;\n\n suspects.push({\n method: route.method,\n path: route.path,\n handlerName: handlerDisplayName(handler as Function),\n sourceFile: route.sourceFile,\n });\n } catch {\n // A diagnostic must never break registration.\n }\n}\n\n/**\n * Every suspect recorded so far, in registration order.\n */\nexport function listPositionalHandlerSuspects(): readonly PositionalHandlerSuspect[] {\n return suspects;\n}\n\n/**\n * Drop the suspects belonging to a route file. Kept in step with\n * `Router.removeRoutesBySourceFile` so an HMR reload that fixes a handler does\n * not keep reporting the version it replaced.\n */\nexport function forgetPositionalHandlerSuspects(sourceFile: string) {\n for (let index = suspects.length - 1; index >= 0; index--) {\n if (suspects[index].sourceFile === sourceFile) {\n suspects.splice(index, 1);\n }\n }\n}\n\n/**\n * Drop every recorded suspect. Intended for tests, which share the router\n * singleton.\n */\nexport function clearPositionalHandlerSuspects() {\n suspects.length = 0;\n}\n\n/**\n * The one-line diagnostic for a single route. Written for someone who has never\n * read Warlock's internals: it names the handler, the route, what is wrong, and\n * the exact edit that fixes it.\n */\nexport function describePositionalHandlerSuspect(suspect: PositionalHandlerSuspect): string {\n return (\n `Handler \"${suspect.handlerName}\" (${suspect.method} ${suspect.path}) looks like ` +\n `the v4 positional signature (request, response). ` +\n `v5 passes a single context object — change it to ({ request, response }).`\n );\n}\n\n/**\n * The full boot-time warning: every suspect, once, in one entry.\n *\n * One boot, one list — not one warning per route, and emphatically not one 500\n * per request. Returns the reported suspects so callers can assert on them.\n */\nexport function reportPositionalHandlerSuspects(logger: Pick<typeof log, \"warn\"> = log) {\n const reported = [...suspects];\n\n if (reported.length === 0) return reported;\n\n const headline =\n reported.length === 1\n ? \"1 route handler looks like the v4 positional signature.\"\n : `${reported.length} route handlers look like the v4 positional signature.`;\n\n logger.warn({\n module: \"router\",\n action: \"handlerSignature\",\n message:\n `${headline} Warlock v5 calls every route handler with a single context object, ` +\n `so a second parameter is always undefined and the request fails as soon as it is used:\\n` +\n reported.map((suspect) => ` - ${describePositionalHandlerSuspect(suspect)}`).join(\"\\n\"),\n context: { handlers: reported },\n });\n\n return reported;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"positional-handler-diagnostics.mjs","names":[],"sources":["../../../../../../../core/src/router/positional-handler-diagnostics.ts"],"sourcesContent":["/**\n * Early detection of route handlers still written against the **v4 positional\n * signature** — `(request, response)` — after v5 moved to a single context\n * object, `({ request, response })`.\n *\n * ## Why this exists\n *\n * A v4 handler is not rejected anywhere. It registers fine, and the router\n * calls it with one argument, so `response` is simply `undefined`. The app only\n * breaks on the first request that reaches it, with:\n *\n * TypeError: Cannot read properties of undefined (reading 'success')\n *\n * That message names nothing the reader can act on — not the route, not the\n * handler, not the change to make. This module turns that late, opaque failure\n * into one explicit list printed at boot, and into a `warlock doctor` check.\n *\n * The break itself is intentional; nothing here restores the positional form.\n * Detection and diagnostics only.\n *\n * ## Why it warns and never throws\n *\n * The signal is a heuristic (see {@link looksLikePositionalHandler}), so a\n * false positive is possible in principle. Turning one into a boot failure\n * would take a working application down over a guess, which is strictly worse\n * than the problem being diagnosed. Everything on this path is therefore a\n * warning: nothing here throws, and nothing here changes how a handler is\n * called. A miss simply degrades to the old runtime `TypeError`.\n */\n\nimport { log } from \"@warlock.js/logger\";\nimport type { Route } from \"./types\";\n\n/**\n * One route whose handler looks like it was written for v4.\n */\nexport type PositionalHandlerSuspect = {\n /** HTTP method the route was registered under. */\n method: Route[\"method\"];\n\n /** Full route path, prefixes already applied. */\n path: string;\n\n /** Handler function name, or `\"(anonymous)\"` when it has none. */\n handlerName: string;\n\n /** Route file the route came from, when the router knows it. */\n sourceFile: string;\n};\n\n/**\n * Collected across the whole registration pass so the warning can be emitted\n * ONCE, as a single list, rather than one line per route (or — as before — one\n * mystery 500 per request).\n */\nconst suspects: PositionalHandlerSuspect[] = [];\n\n/**\n * Split the parameter list of a function's source into top-level parameters.\n *\n * Depth-aware and quote-aware, because a default value may itself contain\n * commas, parentheses, braces or strings — `(request, response = f(a, b))` is\n * two parameters, not three. Anything unbalanced returns `undefined`: refusing\n * to guess is what keeps a parse quirk from becoming a false warning.\n *\n * @param openIndex index of the `(` that opens the parameter list.\n */\nfunction splitTopLevelParameters(source: string, openIndex: number): string[] | undefined {\n const parameters: string[] = [];\n\n let depth = 0;\n let current = \"\";\n let quote = \"\";\n\n for (let index = openIndex; index < source.length; index++) {\n const char = source[index];\n\n if (quote) {\n if (char === \"\\\\\") {\n current += char + (source[index + 1] ?? \"\");\n index++;\n continue;\n }\n\n if (char === quote) {\n quote = \"\";\n }\n\n current += char;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n quote = char;\n current += char;\n continue;\n }\n\n if (char === \"(\" || char === \"[\" || char === \"{\") {\n depth++;\n\n // The paren that opens the list is punctuation, not part of a parameter.\n if (depth === 1) continue;\n\n current += char;\n continue;\n }\n\n if (char === \")\" || char === \"]\" || char === \"}\") {\n depth--;\n\n if (depth === 0) {\n if (current.trim()) {\n parameters.push(current.trim());\n }\n\n return parameters;\n }\n\n current += char;\n continue;\n }\n\n if (char === \",\" && depth === 1) {\n parameters.push(current.trim());\n current = \"\";\n continue;\n }\n\n current += char;\n }\n\n // Never found the closing paren — treat the source as unreadable.\n return undefined;\n}\n\n/**\n * Read a function's declared parameters from its own source.\n *\n * Returns `undefined` whenever the source cannot be trusted, which callers must\n * treat as \"no information\" rather than \"no parameters\":\n *\n * - `Function.prototype.bind` replaces the body with `[native code]`, so every\n * `[Controller, \"method\"]` tuple handler lands here (the router binds them);\n * - native functions likewise;\n * - a class, whose first `(` belongs to a constructor or a method, not to a\n * parameter list;\n * - anything that fails to parse.\n */\nfunction readParameterList(handler: Function): string[] | undefined {\n let source: string;\n\n try {\n source = Function.prototype.toString.call(handler);\n } catch {\n return undefined;\n }\n\n if (source.includes(\"[native code]\")) return undefined;\n\n if (/^\\s*class[\\s{]/.test(source)) return undefined;\n\n // A single parameter needs no parentheses: `ctx => …`, `async ctx => …`.\n const bareArrow = source.match(/^\\s*(?:async\\s+)?([A-Za-z_$][\\w$]*)\\s*=>/);\n\n if (bareArrow) return [bareArrow[1]];\n\n const openIndex = source.indexOf(\"(\");\n\n if (openIndex === -1) return undefined;\n\n return splitTopLevelParameters(source, openIndex);\n}\n\n/**\n * Whether a parameter is written as a destructuring pattern — `{ request }` or\n * `[a, b]` — as opposed to a plain name.\n */\nfunction isDestructured(parameter: string) {\n const start = parameter.trimStart();\n\n return start.startsWith(\"{\") || start.startsWith(\"[\");\n}\n\n/**\n * Does this handler look like it was written for the v4 positional signature?\n *\n * ## The signal, and what it misses\n *\n * `Function.length` alone is not enough, because it stops counting at the first\n * defaulted parameter:\n *\n * | handler | `.length` | actually |\n * | ---------------------------------- | --------- | -------- |\n * | `({ request, response }) => …` | 1 | v5 |\n * | `(ctx) => …` | 1 | v5 |\n * | `(request, response) => …` | 2 | v4 |\n * | `(request, response = x) => …` | 1 | v4 |\n *\n * So the primary signal is the function's own source: a handler is suspect when\n * it declares **two or more** parameters and the first is **not** destructured.\n * Reading the source catches the defaulted row that arity cannot, and rules out\n * the `({ … }, extra)` shape that arity would wrongly flag.\n *\n * When the source is unreadable — a bound or native function — this falls back\n * to `handler.length >= 2`, which `bind` preserves. That is a weaker signal but\n * never a wrong one for the common cases; it only loses the defaulted row.\n *\n * ## What is still missed, by design\n *\n * A one-parameter v4 handler (`(request) => …`) is indistinguishable from a v5\n * `(ctx) => …` and is deliberately not flagged — guessing there would produce\n * false positives on correct code. False positives against the canonical\n * `({ … })` form are essentially nil, and every miss simply leaves today's\n * behaviour in place.\n */\nexport function looksLikePositionalHandler(handler: unknown): boolean {\n if (typeof handler !== \"function\") return false;\n\n const parameters = readParameterList(handler);\n\n if (parameters) {\n return parameters.length >= 2 && !isDestructured(parameters[0]);\n }\n\n return handler.length >= 2;\n}\n\n/**\n * The name to print for a handler.\n *\n * `Function.bind` prefixes the name with `\"bound \"`, and the router binds every\n * `[Controller, \"method\"]` tuple handler. The bare method name is what the user\n * actually wrote, so that is what gets printed.\n */\nfunction handlerDisplayName(handler: Function): string {\n return handler.name?.replace(/^bound /, \"\").trim() || \"(anonymous)\";\n}\n\n/**\n * Inspect one route's handler as it registers, recording it when it looks like\n * the v4 positional form. Called from `Router.add`, so the cost is paid once per\n * route at registration and never per request.\n *\n * Deliberately total: any unexpected failure in the heuristic is swallowed, so a\n * diagnostic can never be the reason a route fails to register.\n */\nexport function inspectHandlerSignature(\n handler: unknown,\n route: { method: Route[\"method\"]; path: string; sourceFile: string },\n) {\n try {\n if (!looksLikePositionalHandler(handler)) return;\n\n suspects.push({\n method: route.method,\n path: route.path,\n handlerName: handlerDisplayName(handler as Function),\n sourceFile: route.sourceFile,\n });\n } catch {\n // A diagnostic must never break registration.\n }\n}\n\n/**\n * Every suspect recorded so far, in registration order.\n */\nexport function listPositionalHandlerSuspects(): readonly PositionalHandlerSuspect[] {\n return suspects;\n}\n\n/**\n * Drop the suspects belonging to a route file. Kept in step with\n * `Router.removeRoutesBySourceFile` so an HMR reload that fixes a handler does\n * not keep reporting the version it replaced.\n */\nexport function forgetPositionalHandlerSuspects(sourceFile: string) {\n for (let index = suspects.length - 1; index >= 0; index--) {\n if (suspects[index].sourceFile === sourceFile) {\n suspects.splice(index, 1);\n }\n }\n}\n\n/**\n * Drop every recorded suspect. Intended for tests, which share the router\n * singleton.\n */\nexport function clearPositionalHandlerSuspects() {\n suspects.length = 0;\n}\n\n/**\n * The one-line diagnostic for a single route. Written for someone who has never\n * read Warlock's internals: it names the handler, the route, what is wrong, and\n * the exact edit that fixes it.\n */\nexport function describePositionalHandlerSuspect(suspect: PositionalHandlerSuspect): string {\n return (\n `Handler \"${suspect.handlerName}\" (${suspect.method} ${suspect.path}) looks like ` +\n `the v4 positional signature (request, response). ` +\n `v5 passes a single context object — change it to ({ request, response }).`\n );\n}\n\n/**\n * The full boot-time warning: every suspect, once, in one entry.\n *\n * One boot, one list — not one warning per route, and emphatically not one 500\n * per request. Returns the reported suspects so callers can assert on them.\n */\nexport function reportPositionalHandlerSuspects(logger: Pick<typeof log, \"warn\"> = log) {\n const reported = [...suspects];\n\n if (reported.length === 0) return reported;\n\n const headline =\n reported.length === 1\n ? \"1 route handler looks like the v4 positional signature.\"\n : `${reported.length} route handlers look like the v4 positional signature.`;\n\n logger.warn({\n module: \"router\",\n action: \"handlerSignature\",\n message:\n `${headline} Warlock v5 calls every route handler with a single context object, ` +\n `so a second parameter is always undefined and the request fails as soon as it is used:\\n` +\n reported.map((suspect) => ` - ${describePositionalHandlerSuspect(suspect)}`).join(\"\\n\"),\n context: { handlers: reported },\n });\n\n return reported;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-registry.d.mts","names":[],"sources":["../../../../../../../core/src/router/route-registry.ts"],"mappings":";;;;;AAuBA;;cAAa,aAAA;EAAA,QACH,MAAA;;EAgDI;;;EAjCL,QAAA,CAAS,MAAA,EAAQ,KAAA;EAfhB;;;EAkCD,aAAA,CAAc,KAAA,EAAO,KAAA;EAnBZ;;;;EA8BT,IAAA,CACL,MAAA,UACA,GAAA;IACG,KAAA,EAAO,KAAA;IAAO,MAAA,EAAQ,MAAA;EAAA;EAAf;;;EAkBL,aAAA
|
|
1
|
+
{"version":3,"file":"route-registry.d.mts","names":[],"sources":["../../../../../../../core/src/router/route-registry.ts"],"mappings":";;;;;AAuBA;;cAAa,aAAA;EAAA,QACH,MAAA;;EAgDI;;;EAjCL,QAAA,CAAS,MAAA,EAAQ,KAAA;EAfhB;;;EAkCD,aAAA,CAAc,KAAA,EAAO,KAAA;EAnBZ;;;;EA8BT,IAAA,CACL,MAAA,UACA,GAAA;IACG,KAAA,EAAO,KAAA;IAAO,MAAA,EAAQ,MAAA;EAAA;EAAf;;;EAkBL,aAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-registry.mjs","names":[],"sources":["../../../../../../../core/src/router/route-registry.ts"],"sourcesContent":["import FindMyWay, { type HTTPMethod, type Instance } from \"find-my-way\";\nimport { normalizeRequestPath } from \"./normalize-request-path\";\nimport type { RequestMethod, Route } from \"./types\";\n\n/**\n * The concrete HTTP verbs an `all` route expands into.\n * Mirrors the set Fastify's `.all()` answers in production, so `router.any()`\n * routes behave identically under the dev server and prod Fastify.\n */\nconst ALL_METHODS: Exclude<RequestMethod, \"all\">[] = [\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"OPTIONS\",\n \"HEAD\",\n];\n\n/**\n * Route Registry\n * Manages dynamic route matching using find-my-way for HMR support\n */\nexport class RouteRegistry {\n private router: Instance<any>;\n\n public constructor() {\n this.router = FindMyWay({\n // Request paths are normalized explicitly in `find`; leaving this off\n // makes that shared normalizer, rather than a second router option, the\n // reason development accepts a terminal slash.\n ignoreTrailingSlash: false,\n caseSensitive: false,\n });\n }\n\n /**\n * Register all routes from the router's internal list\n */\n public register(routes: Route[]): void {\n // Register each route\n for (const route of routes) {\n if (route.method === \"all\") {\n for (const method of ALL_METHODS) {\n this.registerRoute({\n ...route,\n method,\n });\n }\n } else {\n this.registerRoute(route);\n }\n }\n }\n\n /**\n * Register a single route\n */\n public registerRoute(route: Route): void {\n this.router.on(route.method as HTTPMethod, route.path, (req, res, params) => {\n // Store the route and params for later use\n return { route, params };\n });\n }\n\n /**\n * Find a matching route for the given method and URL\n * @returns Matched route with extracted params, or null if no match\n */\n public find(\n method: string,\n url: string,\n ): { route: Route; params: Record<string, string> } | null {\n // Strip query string from URL (find-my-way expects just the path)\n const path = normalizeRequestPath(url.split(\"?\")[0]);\n\n const match = this.router.find(method as HTTPMethod, path);\n\n if (!match) {\n return null;\n }\n\n // find-my-way handler expects (req, res, params, store, searchParams)\n // We only care about the return value which contains { route, params }\n return match.handler(null as any, null as any, match.params, match.store, {});\n }\n\n /**\n * Get all registered routes count (for debugging)\n */\n public getRouteCount(): number {\n return this.router\n .prettyPrint()\n .split(\"\\n\")\n .filter((line) => line.trim()).length;\n }\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,cAA+C;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;AAMA,IAAa,gBAAb,MAA2B;CAGzB,AAAO,cAAc;EACnB,KAAK,SAAS,UAAU;GAItB,qBAAqB;GACrB,eAAe;EACjB,CAAC;CACH;;;;CAKA,AAAO,SAAS,QAAuB;EAErC,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,WAAW,OACnB,KAAK,MAAM,UAAU,aACnB,KAAK,cAAc;GACjB,GAAG;GACH;EACF,CAAC;OAGH,KAAK,cAAc,KAAK;CAG9B;;;;CAKA,AAAO,cAAc,OAAoB;EACvC,KAAK,OAAO,GAAG,MAAM,QAAsB,MAAM,OAAO,KAAK,KAAK,WAAW;GAE3E,OAAO;IAAE;IAAO;GAAO;EACzB,CAAC;CACH;;;;;CAMA,AAAO,KACL,QACA,KACyD;EAEzD,MAAM,OAAO,qBAAqB,IAAI,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"route-registry.mjs","names":[],"sources":["../../../../../../../core/src/router/route-registry.ts"],"sourcesContent":["import FindMyWay, { type HTTPMethod, type Instance } from \"find-my-way\";\nimport { normalizeRequestPath } from \"./normalize-request-path\";\nimport type { RequestMethod, Route } from \"./types\";\n\n/**\n * The concrete HTTP verbs an `all` route expands into.\n * Mirrors the set Fastify's `.all()` answers in production, so `router.any()`\n * routes behave identically under the dev server and prod Fastify.\n */\nconst ALL_METHODS: Exclude<RequestMethod, \"all\">[] = [\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"OPTIONS\",\n \"HEAD\",\n];\n\n/**\n * Route Registry\n * Manages dynamic route matching using find-my-way for HMR support\n */\nexport class RouteRegistry {\n private router: Instance<any>;\n\n public constructor() {\n this.router = FindMyWay({\n // Request paths are normalized explicitly in `find`; leaving this off\n // makes that shared normalizer, rather than a second router option, the\n // reason development accepts a terminal slash.\n ignoreTrailingSlash: false,\n caseSensitive: false,\n });\n }\n\n /**\n * Register all routes from the router's internal list\n */\n public register(routes: Route[]): void {\n // Register each route\n for (const route of routes) {\n if (route.method === \"all\") {\n for (const method of ALL_METHODS) {\n this.registerRoute({\n ...route,\n method,\n });\n }\n } else {\n this.registerRoute(route);\n }\n }\n }\n\n /**\n * Register a single route\n */\n public registerRoute(route: Route): void {\n this.router.on(route.method as HTTPMethod, route.path, (req, res, params) => {\n // Store the route and params for later use\n return { route, params };\n });\n }\n\n /**\n * Find a matching route for the given method and URL\n * @returns Matched route with extracted params, or null if no match\n */\n public find(\n method: string,\n url: string,\n ): { route: Route; params: Record<string, string> } | null {\n // Strip query string from URL (find-my-way expects just the path)\n const path = normalizeRequestPath(url.split(\"?\")[0]);\n\n const match = this.router.find(method as HTTPMethod, path);\n\n if (!match) {\n return null;\n }\n\n // find-my-way handler expects (req, res, params, store, searchParams)\n // We only care about the return value which contains { route, params }\n return match.handler(null as any, null as any, match.params, match.store, {});\n }\n\n /**\n * Get all registered routes count (for debugging)\n */\n public getRouteCount(): number {\n return this.router\n .prettyPrint()\n .split(\"\\n\")\n .filter((line) => line.trim()).length;\n }\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,cAA+C;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;AAMA,IAAa,gBAAb,MAA2B;CAGzB,AAAO,cAAc;EACnB,KAAK,SAAS,UAAU;GAItB,qBAAqB;GACrB,eAAe;EACjB,CAAC;CACH;;;;CAKA,AAAO,SAAS,QAAuB;EAErC,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,WAAW,OACnB,KAAK,MAAM,UAAU,aACnB,KAAK,cAAc;GACjB,GAAG;GACH;EACF,CAAC;OAGH,KAAK,cAAc,KAAK;CAG9B;;;;CAKA,AAAO,cAAc,OAAoB;EACvC,KAAK,OAAO,GAAG,MAAM,QAAsB,MAAM,OAAO,KAAK,KAAK,WAAW;GAE3E,OAAO;IAAE;IAAO;GAAO;EACzB,CAAC;CACH;;;;;CAMA,AAAO,KACL,QACA,KACyD;EAEzD,MAAM,OAAO,qBAAqB,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE;EAEnD,MAAM,QAAQ,KAAK,OAAO,KAAK,QAAsB,IAAI;EAEzD,IAAI,CAAC,OACH,OAAO;EAKT,OAAO,MAAM,QAAQ,MAAa,MAAa,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC;CAC9E;;;;CAKA,AAAO,gBAAwB;EAC7B,OAAO,KAAK,OACT,YAAY,CAAC,CACb,MAAM,IAAI,CAAC,CACX,QAAQ,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC;CACnC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.mts","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"mappings":";;;;;;;cA6Ha,MAAA;;AAAb;;UAIU,MAAA;EA8BqB;;;;;;;;EAAA,QApBrB,aAAA;EAiEgD;;;;EAAA,QA3DhD,2BAAA;EAiII;;;EAAA,eAxHG,QAAA;EAkJJ;;;EAAA,UA7ID,iBAAA,EAAmB,oBAAA;EAqQkC;;;EAAA,UAhQrD,cAAA,EAAgB,MAAA,WAAiB,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,eAAA;EA8QJ;;;;EAAA,UAvQrD,MAAA,EAAQ,YAAA;EA4RiB;;;EAAA,OAnRrB,WAAA,
|
|
1
|
+
{"version":3,"file":"router.d.mts","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.mjs","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"sourcesContent":["import proxy, { type FastifyHttpProxyOptions } from \"@fastify/http-proxy\";\nimport fastifyStatic, { type FastifyStaticOptions } from \"@fastify/static\";\nimport concatRoute from \"@mongez/concat-route\";\nimport { ltrim, merge, toCamelCase, trim } from \"@mongez/reinforcements\";\nimport { isEmpty } from \"@mongez/supportive-is\";\nimport { log } from \"@warlock.js/logger\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { container } from \"../container\";\nimport { Request } from \"../http/request\";\nimport { Response } from \"../http/response\";\nimport { type FastifyInstance } from \"../http/server\";\nimport { describeRouteForLog } from \"./describe-route-for-log\";\nimport { logRequestLifecycle } from \"./log-request-lifecycle\";\nimport {\n forgetPositionalHandlerSuspects,\n inspectHandlerSignature,\n reportPositionalHandlerSuspects,\n} from \"./positional-handler-diagnostics\";\nimport { normalizeRoutePath } from \"./normalize-route-path\";\nimport { RouteBuilder } from \"./route-builder\";\nimport { RouteRegistry } from \"./route-registry\";\nimport { routeNameMethodSuffix } from \"./route-name-method-suffix\";\nimport type {\n GroupedRoutesOptions,\n HttpContext,\n RequestHandler,\n RequestHandlerType,\n RequestHandlerValidation,\n ResourceMethod,\n Route,\n RouteOptions,\n RouteResource,\n RouterGroupCallback,\n RouterStacks,\n} from \"./types\";\n\n/**\n * A Fastify lifecycle hook as it may be declared in a route's `serverOptions`.\n * Fastify accepts a single hook or an array in either position.\n */\ntype RouteLifecycleHook = (\n request: FastifyRequest,\n reply: FastifyReply,\n) => unknown | Promise<unknown>;\n\n/**\n * The pre-handler phases the dev server forwards from a route's `serverOptions`.\n *\n * These are the admission-control phases and they share the `(request, reply)`\n * signature. `preParsing` is deliberately absent — it must return the payload\n * stream, so forwarding it generically would corrupt the body rather than\n * guard it. `bodyLimit` cannot be forwarded at all; Fastify reads it at\n * registration time and no hook can bound a body already being parsed.\n */\nconst FORWARDED_ROUTE_PHASES = [\"onRequest\", \"preValidation\", \"preHandler\"] as const;\n\ntype ForwardedRoutePhase = (typeof FORWARDED_ROUTE_PHASES)[number];\n\n/**\n * The route the dev dispatcher matched, stashed on the request so the hooks and\n * the wildcard handler all act on one match rather than each matching again.\n */\ntype DevMatchedRoute = { route: Route; params: Record<string, string> };\n\ntype DevDispatchRequest = FastifyRequest & {\n matchedDevRoute?: DevMatchedRoute;\n};\n\nfunction toHookList(\n hook: RouteLifecycleHook | RouteLifecycleHook[] | undefined,\n): RouteLifecycleHook[] {\n if (!hook) {\n return [];\n }\n\n return Array.isArray(hook) ? hook : [hook];\n}\n\n/**\n * Run a matched route's hooks for one phase.\n *\n * Returning the reply is how an async Fastify hook signals that it has answered\n * the request; the chain must stop there, so the value is propagated rather\n * than discarded.\n */\nasync function runRouteHooks(\n route: Route,\n phase: ForwardedRoutePhase,\n request: FastifyRequest,\n reply: FastifyReply,\n) {\n const hooks = toHookList(\n route.serverOptions?.[phase] as RouteLifecycleHook | RouteLifecycleHook[] | undefined,\n );\n\n for (const hook of hooks) {\n const outcome = await hook(request, reply);\n\n if (outcome === reply) {\n return reply;\n }\n }\n\n return undefined;\n}\n\n/**\n * Describe one claimant of a route name, for the duplicate-name error.\n *\n * `warlock dev` registers SSR page routes on the same router as API routes, so\n * the two share a single route-name namespace and collide across kinds. The\n * error is only actionable if it says which claimant is which, so `isPage` —\n * the router's only route-kind discriminator — is what separates them here.\n *\n * `sourceFile` is stamped only on routes registered through `withSourceFile`.\n * Routes registered without that scope have no owner, so say the source is\n * unknown rather than printing an empty string.\n */\nfunction describeRouteClaimant(route: Route) {\n const type = route.isPage ? \"page route\" : \"API route\";\n const source = route.sourceFile ? `declared in ${route.sourceFile}` : \"source file unknown\";\n\n return `${type} ${route.method} ${route.path} (${source})`;\n}\n\nexport class Router {\n /**\n * Routes list\n */\n private routes: Route[] = [];\n\n /**\n * Bumped on every mutation of {@link routes}.\n *\n * The dev dispatcher caches a route registry and needs to know when it is\n * stale. Comparing `routes.length` would miss an HMR reload that replaces a\n * route without changing the count — the registry would then keep matching\n * paths that no longer exist. A version counter cannot miss that.\n */\n private routesVersion = 0;\n\n /**\n * In-progress atomic replacement, when one exists. Reads continue to use\n * {@link routes}; mutations and collision checks use this isolated draft.\n */\n private routeReplacementTransaction?: {\n routes: Route[];\n removedSourceFiles: Set<string>;\n addedRoutes: Route[];\n };\n\n /**\n * Router Instance\n */\n private static instance: Router;\n\n /**\n * Static paths\n */\n protected staticDirectories: FastifyStaticOptions[] = [];\n\n /**\n * Event listeners\n */\n protected eventListeners: Record<string, ((router: Router, server: FastifyInstance) => void)[]> =\n {};\n\n /**\n * Stacks\n * Stacks will be used for grouping routes to add prefix, name or middleware\n */\n protected stacks: RouterStacks = {\n prefix: [],\n name: [],\n middleware: [],\n };\n\n /**\n * Get router instance\n */\n public static getInstance() {\n if (!Router.instance) {\n Router.instance = new Router();\n }\n\n return Router.instance;\n }\n\n private constructor() {\n //\n }\n\n /**\n * Listen to router before scan\n */\n public beforeScanning(callback: (router: Router, server: FastifyInstance) => void) {\n this.eventListeners.beforeScan = [...(this.eventListeners.beforeScan || []), callback];\n\n return this;\n }\n\n /**\n * Listen to router after scanning\n */\n public afterScanning(callback: (router: Router, server: FastifyInstance) => void) {\n this.eventListeners.afterScanning = [...(this.eventListeners.afterScanning || []), callback];\n\n return this;\n }\n\n /**\n * Redirect path to another path\n */\n public redirect(from: string, to: string, redirectMode: \"temporary\" | \"permanent\" = \"temporary\") {\n return this.get(from, ({ response }) => {\n response.redirect(to, redirectMode === \"temporary\" ? 302 : 301);\n });\n }\n\n /**\n * Server static folder\n */\n public directory(options: FastifyStaticOptions) {\n this.staticDirectories.push(options);\n\n return this;\n }\n\n /**\n * Serve file\n */\n public file(path: string, location: string, cacheTime?: number) {\n return this.get(path, ({ response }) => {\n response.sendFile(location, cacheTime);\n });\n }\n\n /**\n * Serve cached file, it will cache the file to 1 year by default\n */\n public cachedFile(path: string, location: string, cacheTime?: number) {\n return this.get(path, ({ response }) => {\n response.sendCachedFile(location, cacheTime);\n });\n }\n\n /**\n * Serve list of files\n */\n public files(files: Record<string, string>, cacheTime?: number) {\n for (const [path, location] of Object.entries(files)) {\n this.get(path, ({ response }) => {\n response.sendFile(location, cacheTime);\n });\n }\n }\n\n /**\n * Serve list of cached files, it will cache the file to 1 year by default\n */\n public cachedFiles(files: Record<string, string>, cacheTime?: number) {\n for (const [path, location] of Object.entries(files)) {\n this.get(path, ({ response }) => {\n response.sendCachedFile(location, cacheTime);\n });\n }\n }\n\n /**\n * Add proxy route\n */\n public proxy(\n path: string,\n baseUrl: string,\n options?: Omit<FastifyHttpProxyOptions, \"prefix\" | \"upstream\">,\n ): this;\n public proxy(options: FastifyHttpProxyOptions): this;\n public proxy(...args: any[]) {\n this.beforeScanning((_router, server) => {\n if (args.length === 1) {\n server.register(proxy, args[0]);\n } else {\n server.register(proxy, {\n prefix: args[0],\n upstream: args[1],\n ...args[2],\n });\n }\n });\n\n return this;\n }\n\n /**\n * Add route to routes list\n */\n public add(\n method: Route[\"method\"],\n path: string | string[],\n handler: RequestHandlerType,\n options: RouteOptions = {},\n ) {\n if (Array.isArray(path)) {\n path.forEach((p) => this.add(method, p, handler, options));\n return this;\n }\n\n const prefix = this.stacks.prefix.reduce((path, prefix) => {\n return concatRoute(path, prefix);\n }, \"\");\n\n const name = this.stacks.name.reduceRight(\n (name, prefixName) => {\n return trim(prefixName + \".\" + name, \".\");\n },\n options.name || trim(path.replace(/\\//g, \".\"), \".\"),\n );\n\n // The one place a route path becomes canonical. `normalizeRoutePath` is\n // exported so the build-time page-route manifest can produce the very same\n // string without registering anything.\n path = normalizeRoutePath(prefix, path);\n\n const middlewarePrecedence = options.middlewarePrecedence || \"after\";\n\n if (middlewarePrecedence === \"before\") {\n options.middleware = [...(options.middleware || []), ...this.stacks.middleware];\n } else {\n options.middleware = [...this.stacks.middleware, ...(options.middleware || [])];\n }\n\n if (Array.isArray(handler)) {\n const [controller, action] = handler;\n\n if (typeof controller[action] !== \"function\") {\n throw new Error(\n `Invalid controller action \"${action}\" for controller \"${controller.constructor.name}\"`,\n );\n }\n\n handler = controller[action].bind(controller) as RequestHandler;\n\n if (!handler.validation) {\n handler.validation = {};\n if (controller[`${action}ValidationSchema`]) {\n handler.validation.schema = controller[`${action}ValidationSchema`]();\n }\n\n if (controller[`${action}Validate`]) {\n handler.validation.validate = controller[`${action}Validate`];\n }\n }\n }\n\n const routeData: Route = {\n method,\n path,\n handler,\n ...options,\n name,\n rateLimit: options.rateLimit,\n $prefix: prefix || \"/\",\n // it must be a new array to avoid modifying the original array\n $prefixStack: [...this.stacks.prefix],\n // Inject source file from stacks if set\n sourceFile: this.stacks.sourceFile || \"\",\n };\n\n const routes = this.routeReplacementTransaction?.routes ?? this.routes;\n\n if (routeData.name) {\n // check if the name exists\n const route = routes.find((route) => route.name === routeData.name);\n\n if (route) {\n // check again if the route name exists with the same method\n if (route.method === routeData.method) {\n throw new Error(\n `Route name \"${routeData.name}\" is already taken.\\n` +\n ` already registered: ${describeRouteClaimant(route)}\\n` +\n ` now being added: ${describeRouteClaimant(routeData)}\\n` +\n `Pages and API routes share one route-name namespace, so a name can only be ` +\n `claimed once. Rename one of them via the \"name\" route option.`,\n );\n } else {\n routeData.name += routeNameMethodSuffix(routeData.method);\n }\n }\n }\n\n // Registration-time only — nothing here runs per request. Suspects are\n // collected now and reported together at boot (see `scan`), so a route file\n // written against v4 produces one legible list instead of a mystery 500 per\n // request. Recorded after the collision check so a route that never lands\n // is never reported.\n if (this.routeReplacementTransaction) {\n this.routeReplacementTransaction.addedRoutes.push(routeData);\n } else {\n inspectHandlerSignature(routeData.handler, routeData);\n }\n\n routes.push(routeData);\n\n if (!this.routeReplacementTransaction) {\n this.routesVersion++;\n }\n\n return this;\n }\n\n /**\n * Add a request that accepts all methods\n */\n public any(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"all\" as Route[\"method\"], path, handler, options);\n }\n\n /**\n * Add get request method\n */\n public get(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"GET\", path, handler, options);\n }\n\n /**\n * Add post request method\n */\n public post(path: string | string[], handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"POST\", path, handler, options);\n }\n\n /**\n * Add put request method\n */\n public put(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"PUT\", path, handler, options);\n }\n\n /**\n * Add delete request method\n */\n public delete(path: string | string[], handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"DELETE\", path, handler, options);\n }\n\n /**\n * Add patch request method\n */\n public patch(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"PATCH\", path, handler, options);\n }\n\n /**\n * Add head request method\n */\n public head(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"HEAD\", path, handler, options);\n }\n\n /**\n * Add options request method\n */\n public options(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"OPTIONS\", path, handler, options);\n }\n\n /**\n * Get a chainable route builder for the same route path\n */\n public route(path: string, options: RouteOptions = {}) {\n return new RouteBuilder(this, path, options);\n }\n\n /**\n * Add full restful resource routes\n * This method will generate the following routes:\n * 1. GET /path: list all resources\n * 2. GET /path/:id: get a single resource\n * 3. POST /path: create a new resource\n * 4. PUT /path/:id: update a resource\n * 5. DELETE /path/:id: delete a resource\n * 6. PATCH /path/:id: update a resource partially\n */\n public restfulResource(\n path: string,\n resource: RouteResource,\n options: RouteOptions & {\n only?: ResourceMethod[];\n except?: ResourceMethod[];\n replace?: Partial<Record<ResourceMethod, RequestHandler>> & {\n bulkDelete?: RequestHandler;\n };\n } = {},\n ) {\n // Derive the base resource name from the path before the group below\n // clears it for use as the routes' relative path. `options.name`, when\n // given, replaces the path-derived name entirely (see the explicit\n // `name` passed to `group()`) rather than composing with it.\n const baseResourceName = options.name || toCamelCase(ltrim(path, \"/\"));\n\n return this.group({ prefix: path, name: baseResourceName }, () => {\n const path = \"\";\n\n // clone the resource so we don't mess up with it\n const routeResource = resource;\n\n const isAcceptableResource = (type: ResourceMethod) => {\n return Boolean(\n // check if the route is not excluded\n (!options.except || !options.except.includes(type)) &&\n // check if the only option is set and the route is included\n (!options.only || options.only.includes(type)),\n );\n };\n\n if (routeResource.list && isAcceptableResource(\"list\")) {\n this.get(path, options.replace?.list || routeResource.list.bind(routeResource), {\n ...options,\n name: \"list\",\n restful: true,\n });\n }\n\n if (routeResource.get && isAcceptableResource(\"get\")) {\n this.get(path + \"/:id\", options.replace?.get || routeResource.get.bind(routeResource), {\n ...options,\n name: \"single\",\n restful: true,\n });\n }\n\n if (routeResource.create && isAcceptableResource(\"create\")) {\n const handler = options.replace?.create || this.manageValidation(routeResource, \"create\");\n\n this.post(path, handler, {\n ...options,\n name: \"create\",\n restful: true,\n });\n }\n\n if (routeResource.update && isAcceptableResource(\"update\")) {\n const handler = options.replace?.update || this.manageValidation(routeResource, \"update\");\n\n this.put(path + \"/:id\", handler, {\n ...options,\n name: \"update\",\n restful: true,\n });\n }\n\n if (routeResource.patch && isAcceptableResource(\"patch\")) {\n const handler = options.replace?.patch || this.manageValidation(routeResource, \"patch\");\n\n this.patch(path + \"/:id\", handler, {\n ...options,\n name: \"patch\",\n restful: true,\n });\n }\n\n if (routeResource.delete && isAcceptableResource(\"delete\")) {\n this.delete(\n path + \"/:id\",\n options.replace?.delete || routeResource.delete.bind(routeResource),\n {\n ...options,\n name: \"delete\",\n restful: true,\n },\n );\n }\n\n if (routeResource.bulkDelete && isAcceptableResource(\"delete\")) {\n this.delete(\n path,\n options.replace?.bulkDelete || routeResource.bulkDelete.bind(routeResource),\n {\n ...options,\n name: \"bulkDelete\",\n restful: true,\n },\n );\n }\n\n return this;\n });\n }\n\n /**\n * Group routes with options\n */\n public group(options: GroupedRoutesOptions, callback: RouterGroupCallback) {\n const {\n prefix,\n // name must always be defined because\n // if there are multiple groups without name\n // they might generate the same route name\n // thus causing an error\n // in this case we need always to make sure that\n // the name is always defined.\n name = prefix ? trim(prefix.replace(/\\//g, \".\"), \".\") : undefined,\n middleware,\n } = options;\n\n if (prefix) {\n this.stacks.prefix.push(prefix);\n }\n\n if (name) {\n this.stacks.name.push(name);\n }\n\n if (middleware) {\n this.stacks.middleware.push(...middleware);\n }\n\n try {\n callback();\n } finally {\n // Always pop/splice this group's stacks, even if the callback throws,\n // so a throwing group never leaks state onto the process-global singleton.\n if (prefix) {\n this.stacks.prefix.pop();\n }\n\n if (name) {\n this.stacks.name.pop();\n }\n\n if (middleware) {\n this.stacks.middleware.splice(\n this.stacks.middleware.length - middleware.length,\n middleware.length,\n );\n }\n }\n\n return this;\n }\n\n /**\n * Add prefix to all routes in the given callback\n */\n public prefix(prefix: string, callback: () => void) {\n return this.group({ prefix }, callback);\n }\n\n /**\n * Add API version prefix to all routes in the given callback\n * Automatically formats the version as /v{version}\n * @example\n * router.version(\"1\", () => {\n * router.get(\"/users\", getUsersV1); // /v1/users\n * });\n *\n * router.version(\"2\", () => {\n * router.get(\"/users\", getUsersV2); // /v2/users\n * });\n */\n public version(version: string | number, callback: () => void) {\n return this.prefix(`/v${version}`, callback);\n }\n\n /**\n * Wrap route additions with a source file path\n * Used for tracking which routes come from which file (for HMR)\n * @param sourceFile Relative path to the source file (e.g., \"src/app/users/routes.ts\")\n * @param callback Function that adds routes (will have sourceFile injected)\n */\n public async withSourceFile<T = any>(\n sourceFile: string,\n callback: () => T | Promise<T>,\n ): Promise<T | undefined> {\n // Set source file in stacks\n this.stacks.sourceFile = sourceFile;\n\n try {\n // Execute callback (routes added here will have sourceFile injected)\n return await callback();\n } catch (error) {\n // Log loudly, then RETHROW. Swallowing here silently dropped a whole\n // route file (duplicate name, invalid action, import error) so the\n // surface 404'd with no boot error. The caller decides what a throw\n // means: at boot it aborts the boot, during HMR the dev server's\n // batch-reload handler catches it, prints it, and keeps serving.\n log.error({\n module: \"router\",\n action: \"registerRoutes\",\n message: `Failed to register routes from \"${sourceFile}\"`,\n context: { sourceFile, error },\n });\n\n throw error;\n } finally {\n // Always clear source file from stacks so a throwing file never leaks\n // its sourceFile onto the process-global singleton.\n delete this.stacks.sourceFile;\n }\n }\n\n /**\n * Remove all routes that belong to a specific source file\n * Used when reloading routes files via HMR\n * @param sourceFile Relative path to the source file\n */\n public removeRoutesBySourceFile(sourceFile: string): void {\n if (this.routeReplacementTransaction) {\n this.routeReplacementTransaction.routes = this.routeReplacementTransaction.routes.filter(\n (route) => route.sourceFile !== sourceFile,\n );\n this.routeReplacementTransaction.removedSourceFiles.add(sourceFile);\n return;\n }\n\n this.routes = this.routes.filter((route) => route.sourceFile !== sourceFile);\n\n // Keep the signature diagnostics in step, so a reload that fixes a handler\n // stops reporting the version it replaced.\n forgetPositionalHandlerSuspects(sourceFile);\n\n this.routesVersion++;\n }\n\n /**\n * Atomically replace every route owned by the given exact source-file keys.\n *\n * The installer mutates an isolated draft. Public reads keep seeing the old\n * live table across awaits, and a failed installer discards every draft\n * mutation. A successful installer swaps the whole draft in one synchronous\n * commit and invalidates the dev route registry exactly once.\n */\n public async replaceRoutesBySourceFiles<T>(\n sourceFiles: Iterable<string>,\n install: () => T | Promise<T>,\n ): Promise<T> {\n if (this.routeReplacementTransaction) {\n throw new Error(\"Cannot nest route replacement transactions\");\n }\n\n const removedSourceFiles = new Set(sourceFiles);\n\n this.routeReplacementTransaction = {\n routes: this.routes.filter((route) => !removedSourceFiles.has(route.sourceFile)),\n removedSourceFiles,\n addedRoutes: [],\n };\n\n try {\n const result = await install();\n const transaction = this.routeReplacementTransaction!;\n\n this.routes = transaction.routes;\n this.routeReplacementTransaction = undefined;\n\n // Diagnostic state follows the same commit boundary as the route table.\n // Added routes were deliberately not inspected while still speculative.\n for (const sourceFile of transaction.removedSourceFiles) {\n forgetPositionalHandlerSuspects(sourceFile);\n }\n\n const committedRoutes = new Set(transaction.routes);\n\n for (const route of transaction.addedRoutes) {\n if (committedRoutes.has(route)) {\n inspectHandlerSignature(route.handler, route);\n }\n }\n\n this.routesVersion++;\n\n return result;\n } catch (error) {\n this.routeReplacementTransaction = undefined;\n throw error;\n }\n }\n\n /**\n * Manage validation system for the given resource\n */\n private manageValidation(resource: RouteResource, method: \"create\" | \"update\" | \"patch\") {\n const handler = resource[method]?.bind(resource) as RequestHandler;\n\n const methodValidation = resource?.validation?.[method];\n\n if (method === \"patch\") {\n handler.validation = methodValidation;\n\n if (handler.validation?.validate) {\n handler.validation.validate = handler.validation.validate.bind(resource);\n }\n\n if (resource.validation?.patch) {\n handler.validation = merge(resource.validation.patch, handler.validation);\n }\n\n return handler;\n }\n\n if (!resource.validation || (!methodValidation && !resource.validation.all)) return handler;\n\n if (resource.validation.all) {\n const validationMethods = {\n all: resource?.validation?.all?.validate,\n [method]: methodValidation?.validate,\n };\n\n const validation: RequestHandlerValidation = {};\n\n if (resource.validation.all.schema || methodValidation?.schema) {\n if (!methodValidation?.schema && resource.validation.all.schema) {\n // Case 2: Only all.schema exists - clone it for this method\n validation.schema = resource.validation.all.schema;\n } else if (methodValidation?.schema && resource.validation.all.schema) {\n // Case 3: Both exist - merge them (all is base, method overrides)\n validation.schema = resource.validation.all.schema.merge(methodValidation.schema);\n } else if (methodValidation?.schema && !resource.validation.all.schema) {\n // Case 1: Only method schema exists - use it directly\n validation.schema = methodValidation.schema;\n }\n // Case 4: Neither exists - handled by outer if being false\n }\n\n if (validationMethods.all || validationMethods[method]) {\n validation.validate = async ({ request, response }: HttpContext) => {\n if (validationMethods.all) {\n const output = await validationMethods.all.call(resource, { request, response });\n\n if (output) return output;\n }\n\n if (validationMethods[method]) {\n return await validationMethods[method]?.call(resource, { request, response });\n }\n\n return;\n };\n }\n\n if (!isEmpty(validation)) {\n handler.validation = validation;\n }\n } else {\n handler.validation = resource.validation[method];\n\n if (handler.validation?.validate) {\n handler.validation.validate = handler.validation.validate.bind(resource);\n }\n }\n\n return handler;\n }\n\n /**\n * Get all routes list\n */\n public list() {\n return this.routes;\n }\n\n /**\n * Number of routes currently registered on the singleton.\n *\n * Used as a readiness signal: a successful boot that ends up with zero\n * routes almost always means a route module failed to register silently\n * (see `withSourceFile`), so callers can surface that instead of letting\n * the whole surface 404.\n */\n public routeCount(): number {\n return this.routes.length;\n }\n\n /**\n * Register routes to the server\n */\n public scan(server: FastifyInstance) {\n // Every route is registered by the time we scan, so this is the first\n // moment the whole list can be reported at once — and it is still before a\n // single request is served.\n reportPositionalHandlerSuspects();\n\n this.eventListeners.beforeScan?.forEach((callback) => callback(this, server));\n\n this.routes.forEach((route) => {\n const requestMethod = route.method.toLowerCase();\n const requestMethodFunction = server[requestMethod].bind(server);\n\n const options = {\n ...route.serverOptions,\n config: {\n ...route.serverOptions?.config,\n ...(route.rateLimit && { rateLimit: route.rateLimit }),\n },\n };\n\n requestMethodFunction(\n route.path,\n options,\n async (baseRequest: FastifyRequest, reply: FastifyReply) => {\n const { output, response } = await this.handleRoute(route)(baseRequest, reply);\n\n return output || response.baseResponse;\n },\n );\n });\n\n for (const directoryOptions of this.staticDirectories) {\n server.register(fastifyStatic, {\n ...directoryOptions,\n decorateReply: false,\n });\n }\n\n this.eventListeners.afterScanning?.forEach((callback) => callback(this, server));\n }\n\n /**\n * Scan routes for the development server\n * Uses wildcard routing with find-my-way for HMR support\n */\n public scanDevServer(server: FastifyInstance) {\n // Same boot-time report as production `scan`. Routes reloaded later by HMR\n // are still recorded, but only surface on the next scan or via\n // `warlock doctor`.\n reportPositionalHandlerSuspects();\n\n this.eventListeners.beforeScan?.forEach((callback) => callback(this, server));\n\n let routeRegistry: RouteRegistry | undefined;\n let registryVersion = -1;\n\n // Rebuilt when the route table changes rather than per request. Building it\n // inside the handler re-registered every route on every hit — and `all`\n // routes expand into seven registrations each. Keyed on `routesVersion`\n // rather than on the route count, so an HMR reload that swaps a route\n // without changing the count still invalidates.\n const resolveRouteRegistry = () => {\n if (routeRegistry && registryVersion === this.routesVersion) {\n return routeRegistry;\n }\n\n routeRegistry = new RouteRegistry();\n\n routeRegistry.register(this.routes);\n\n registryVersion = this.routesVersion;\n\n return routeRegistry;\n };\n\n // Matching happens here, in Fastify's own onRequest phase, so a route's\n // `serverOptions.onRequest` still runs BEFORE body parsing. Production\n // `scan()` gets that from per-route registration; the wildcard dispatcher\n // has no per-route slot, so without this the hook silently never runs.\n server.addHook(\n \"onRequest\",\n async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = resolveRouteRegistry().find(fastifyRequest.method, fastifyRequest.url);\n\n if (!match) {\n return undefined;\n }\n\n (fastifyRequest as DevDispatchRequest).matchedDevRoute = match;\n\n fastifyRequest.params = match.params;\n\n return runRouteHooks(match.route, \"onRequest\", fastifyRequest, fastifyReply);\n },\n );\n\n for (const phase of FORWARDED_ROUTE_PHASES.filter((name) => name !== \"onRequest\")) {\n server.addHook(phase, async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = (fastifyRequest as DevDispatchRequest).matchedDevRoute;\n\n if (!match) {\n return undefined;\n }\n\n return runRouteHooks(match.route, phase, fastifyRequest, fastifyReply);\n });\n }\n\n // Shared handler for wildcard routing\n const wildcardHandler = async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = (fastifyRequest as DevDispatchRequest).matchedDevRoute;\n\n // No match found - return 404\n if (!match) {\n return fastifyReply.code(404).send({\n error: \"Route not found\",\n path: fastifyRequest.url,\n method: fastifyRequest.method,\n });\n }\n\n try {\n // Call the matched route handler\n const { output, response } = await this.handleRoute(match.route)(\n fastifyRequest,\n fastifyReply,\n );\n\n return output || response.baseResponse;\n } catch (error) {\n log.error(\"router\", \"dev-dispatch\", error as Error, {\n method: fastifyRequest.method,\n url: fastifyRequest.url,\n });\n\n throw error;\n }\n };\n\n // Register wildcard route for all methods EXCEPT OPTIONS (to avoid conflict with CORS)\n // OPTIONS is handled by @fastify/cors plugin for preflight requests\n const methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"] as const;\n for (const method of methods) {\n server.route({\n method,\n url: \"*\",\n handler: wildcardHandler,\n });\n }\n\n // Register static directories\n for (const directoryOptions of this.staticDirectories) {\n server.register(fastifyStatic, {\n ...directoryOptions,\n decorateReply: false,\n });\n }\n\n this.eventListeners.afterScanning?.forEach((callback) => callback(this, server));\n }\n\n /**\n * Get the route path for the given route name\n */\n public getRoute(name: string, params: any = {}) {\n const route = this.routes.find((route) => route.name === name);\n\n if (!route) {\n throw new Error(`Route name \"${name}\" not found`);\n }\n\n let path = route.path;\n\n if (route.path.includes(\":\")) {\n Object.keys(params).forEach((key) => {\n path = path.replace(\":\" + key, params[key]);\n });\n }\n\n return path;\n }\n\n /**\n * Handle the given route\n */\n private handleRoute(route: Route) {\n return async (fastifyRequest: FastifyRequest, fastifyResponse: FastifyReply) => {\n const request = new Request();\n const response = new Response();\n response.setResponse(fastifyResponse);\n request.response = response;\n\n response.request = request;\n\n request.setRequest(fastifyRequest).setRoute(route);\n\n /*\n Both ends of the entry, not just the start — see\n `log-request-lifecycle.ts`. A start line on its own made a hung request\n and an instant one look identical, and the completion line is what makes\n a MISSING one legible as a hang.\n */\n const result = await logRequestLifecycle(\n {\n info: entry => log.info(entry),\n warn: entry => log.warn(entry),\n error: entry => log.error(entry),\n now: () => performance.now(),\n },\n {\n module: \"route\",\n action: describeRouteForLog(route.method, route.path),\n requestId: request.id,\n context: { request, response },\n // Read after the run: the status is decided during the request.\n statusCode: () => fastifyResponse.statusCode,\n },\n () => request.execute(),\n );\n\n return {\n output: result,\n response,\n request,\n };\n };\n }\n}\n\nexport const router = Router.getInstance();\n\ncontainer.set(\"router\", router);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.mjs","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"sourcesContent":["import proxy, { type FastifyHttpProxyOptions } from \"@fastify/http-proxy\";\nimport fastifyStatic, { type FastifyStaticOptions } from \"@fastify/static\";\nimport concatRoute from \"@mongez/concat-route\";\nimport { ltrim, merge, toCamelCase, trim } from \"@mongez/reinforcements\";\nimport { isEmpty } from \"@mongez/supportive-is\";\nimport { log } from \"@warlock.js/logger\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { container } from \"../container\";\nimport { Request } from \"../http/request\";\nimport { Response } from \"../http/response\";\nimport { type FastifyInstance } from \"../http/server\";\nimport { describeRouteForLog } from \"./describe-route-for-log\";\nimport { logRequestLifecycle } from \"./log-request-lifecycle\";\nimport {\n forgetPositionalHandlerSuspects,\n inspectHandlerSignature,\n reportPositionalHandlerSuspects,\n} from \"./positional-handler-diagnostics\";\nimport { normalizeRoutePath } from \"./normalize-route-path\";\nimport { RouteBuilder } from \"./route-builder\";\nimport { RouteRegistry } from \"./route-registry\";\nimport { routeNameMethodSuffix } from \"./route-name-method-suffix\";\nimport type {\n GroupedRoutesOptions,\n HttpContext,\n RequestHandler,\n RequestHandlerType,\n RequestHandlerValidation,\n ResourceMethod,\n Route,\n RouteOptions,\n RouteResource,\n RouterGroupCallback,\n RouterStacks,\n} from \"./types\";\n\n/**\n * A Fastify lifecycle hook as it may be declared in a route's `serverOptions`.\n * Fastify accepts a single hook or an array in either position.\n */\ntype RouteLifecycleHook = (\n request: FastifyRequest,\n reply: FastifyReply,\n) => unknown | Promise<unknown>;\n\n/**\n * The pre-handler phases the dev server forwards from a route's `serverOptions`.\n *\n * These are the admission-control phases and they share the `(request, reply)`\n * signature. `preParsing` is deliberately absent — it must return the payload\n * stream, so forwarding it generically would corrupt the body rather than\n * guard it. `bodyLimit` cannot be forwarded at all; Fastify reads it at\n * registration time and no hook can bound a body already being parsed.\n */\nconst FORWARDED_ROUTE_PHASES = [\"onRequest\", \"preValidation\", \"preHandler\"] as const;\n\ntype ForwardedRoutePhase = (typeof FORWARDED_ROUTE_PHASES)[number];\n\n/**\n * The route the dev dispatcher matched, stashed on the request so the hooks and\n * the wildcard handler all act on one match rather than each matching again.\n */\ntype DevMatchedRoute = { route: Route; params: Record<string, string> };\n\ntype DevDispatchRequest = FastifyRequest & {\n matchedDevRoute?: DevMatchedRoute;\n};\n\nfunction toHookList(\n hook: RouteLifecycleHook | RouteLifecycleHook[] | undefined,\n): RouteLifecycleHook[] {\n if (!hook) {\n return [];\n }\n\n return Array.isArray(hook) ? hook : [hook];\n}\n\n/**\n * Run a matched route's hooks for one phase.\n *\n * Returning the reply is how an async Fastify hook signals that it has answered\n * the request; the chain must stop there, so the value is propagated rather\n * than discarded.\n */\nasync function runRouteHooks(\n route: Route,\n phase: ForwardedRoutePhase,\n request: FastifyRequest,\n reply: FastifyReply,\n) {\n const hooks = toHookList(\n route.serverOptions?.[phase] as RouteLifecycleHook | RouteLifecycleHook[] | undefined,\n );\n\n for (const hook of hooks) {\n const outcome = await hook(request, reply);\n\n if (outcome === reply) {\n return reply;\n }\n }\n\n return undefined;\n}\n\n/**\n * Describe one claimant of a route name, for the duplicate-name error.\n *\n * `warlock dev` registers SSR page routes on the same router as API routes, so\n * the two share a single route-name namespace and collide across kinds. The\n * error is only actionable if it says which claimant is which, so `isPage` —\n * the router's only route-kind discriminator — is what separates them here.\n *\n * `sourceFile` is stamped only on routes registered through `withSourceFile`.\n * Routes registered without that scope have no owner, so say the source is\n * unknown rather than printing an empty string.\n */\nfunction describeRouteClaimant(route: Route) {\n const type = route.isPage ? \"page route\" : \"API route\";\n const source = route.sourceFile ? `declared in ${route.sourceFile}` : \"source file unknown\";\n\n return `${type} ${route.method} ${route.path} (${source})`;\n}\n\nexport class Router {\n /**\n * Routes list\n */\n private routes: Route[] = [];\n\n /**\n * Bumped on every mutation of {@link routes}.\n *\n * The dev dispatcher caches a route registry and needs to know when it is\n * stale. Comparing `routes.length` would miss an HMR reload that replaces a\n * route without changing the count — the registry would then keep matching\n * paths that no longer exist. A version counter cannot miss that.\n */\n private routesVersion = 0;\n\n /**\n * In-progress atomic replacement, when one exists. Reads continue to use\n * {@link routes}; mutations and collision checks use this isolated draft.\n */\n private routeReplacementTransaction?: {\n routes: Route[];\n removedSourceFiles: Set<string>;\n addedRoutes: Route[];\n };\n\n /**\n * Router Instance\n */\n private static instance: Router;\n\n /**\n * Static paths\n */\n protected staticDirectories: FastifyStaticOptions[] = [];\n\n /**\n * Event listeners\n */\n protected eventListeners: Record<string, ((router: Router, server: FastifyInstance) => void)[]> =\n {};\n\n /**\n * Stacks\n * Stacks will be used for grouping routes to add prefix, name or middleware\n */\n protected stacks: RouterStacks = {\n prefix: [],\n name: [],\n middleware: [],\n };\n\n /**\n * Get router instance\n */\n public static getInstance() {\n if (!Router.instance) {\n Router.instance = new Router();\n }\n\n return Router.instance;\n }\n\n private constructor() {\n //\n }\n\n /**\n * Listen to router before scan\n */\n public beforeScanning(callback: (router: Router, server: FastifyInstance) => void) {\n this.eventListeners.beforeScan = [...(this.eventListeners.beforeScan || []), callback];\n\n return this;\n }\n\n /**\n * Listen to router after scanning\n */\n public afterScanning(callback: (router: Router, server: FastifyInstance) => void) {\n this.eventListeners.afterScanning = [...(this.eventListeners.afterScanning || []), callback];\n\n return this;\n }\n\n /**\n * Redirect path to another path\n */\n public redirect(from: string, to: string, redirectMode: \"temporary\" | \"permanent\" = \"temporary\") {\n return this.get(from, ({ response }) => {\n response.redirect(to, redirectMode === \"temporary\" ? 302 : 301);\n });\n }\n\n /**\n * Server static folder\n */\n public directory(options: FastifyStaticOptions) {\n this.staticDirectories.push(options);\n\n return this;\n }\n\n /**\n * Serve file\n */\n public file(path: string, location: string, cacheTime?: number) {\n return this.get(path, ({ response }) => {\n response.sendFile(location, cacheTime);\n });\n }\n\n /**\n * Serve cached file, it will cache the file to 1 year by default\n */\n public cachedFile(path: string, location: string, cacheTime?: number) {\n return this.get(path, ({ response }) => {\n response.sendCachedFile(location, cacheTime);\n });\n }\n\n /**\n * Serve list of files\n */\n public files(files: Record<string, string>, cacheTime?: number) {\n for (const [path, location] of Object.entries(files)) {\n this.get(path, ({ response }) => {\n response.sendFile(location, cacheTime);\n });\n }\n }\n\n /**\n * Serve list of cached files, it will cache the file to 1 year by default\n */\n public cachedFiles(files: Record<string, string>, cacheTime?: number) {\n for (const [path, location] of Object.entries(files)) {\n this.get(path, ({ response }) => {\n response.sendCachedFile(location, cacheTime);\n });\n }\n }\n\n /**\n * Add proxy route\n */\n public proxy(\n path: string,\n baseUrl: string,\n options?: Omit<FastifyHttpProxyOptions, \"prefix\" | \"upstream\">,\n ): this;\n public proxy(options: FastifyHttpProxyOptions): this;\n public proxy(...args: any[]) {\n this.beforeScanning((_router, server) => {\n if (args.length === 1) {\n server.register(proxy, args[0]);\n } else {\n server.register(proxy, {\n prefix: args[0],\n upstream: args[1],\n ...args[2],\n });\n }\n });\n\n return this;\n }\n\n /**\n * Add route to routes list\n */\n public add(\n method: Route[\"method\"],\n path: string | string[],\n handler: RequestHandlerType,\n options: RouteOptions = {},\n ) {\n if (Array.isArray(path)) {\n path.forEach((p) => this.add(method, p, handler, options));\n return this;\n }\n\n const prefix = this.stacks.prefix.reduce((path, prefix) => {\n return concatRoute(path, prefix);\n }, \"\");\n\n const name = this.stacks.name.reduceRight(\n (name, prefixName) => {\n return trim(prefixName + \".\" + name, \".\");\n },\n options.name || trim(path.replace(/\\//g, \".\"), \".\"),\n );\n\n // The one place a route path becomes canonical. `normalizeRoutePath` is\n // exported so the build-time page-route manifest can produce the very same\n // string without registering anything.\n path = normalizeRoutePath(prefix, path);\n\n const middlewarePrecedence = options.middlewarePrecedence || \"after\";\n\n if (middlewarePrecedence === \"before\") {\n options.middleware = [...(options.middleware || []), ...this.stacks.middleware];\n } else {\n options.middleware = [...this.stacks.middleware, ...(options.middleware || [])];\n }\n\n if (Array.isArray(handler)) {\n const [controller, action] = handler;\n\n if (typeof controller[action] !== \"function\") {\n throw new Error(\n `Invalid controller action \"${action}\" for controller \"${controller.constructor.name}\"`,\n );\n }\n\n handler = controller[action].bind(controller) as RequestHandler;\n\n if (!handler.validation) {\n handler.validation = {};\n if (controller[`${action}ValidationSchema`]) {\n handler.validation.schema = controller[`${action}ValidationSchema`]();\n }\n\n if (controller[`${action}Validate`]) {\n handler.validation.validate = controller[`${action}Validate`];\n }\n }\n }\n\n const routeData: Route = {\n method,\n path,\n handler,\n ...options,\n name,\n rateLimit: options.rateLimit,\n $prefix: prefix || \"/\",\n // it must be a new array to avoid modifying the original array\n $prefixStack: [...this.stacks.prefix],\n // Inject source file from stacks if set\n sourceFile: this.stacks.sourceFile || \"\",\n };\n\n const routes = this.routeReplacementTransaction?.routes ?? this.routes;\n\n if (routeData.name) {\n // check if the name exists\n const route = routes.find((route) => route.name === routeData.name);\n\n if (route) {\n // check again if the route name exists with the same method\n if (route.method === routeData.method) {\n throw new Error(\n `Route name \"${routeData.name}\" is already taken.\\n` +\n ` already registered: ${describeRouteClaimant(route)}\\n` +\n ` now being added: ${describeRouteClaimant(routeData)}\\n` +\n `Pages and API routes share one route-name namespace, so a name can only be ` +\n `claimed once. Rename one of them via the \"name\" route option.`,\n );\n } else {\n routeData.name += routeNameMethodSuffix(routeData.method);\n }\n }\n }\n\n // Registration-time only — nothing here runs per request. Suspects are\n // collected now and reported together at boot (see `scan`), so a route file\n // written against v4 produces one legible list instead of a mystery 500 per\n // request. Recorded after the collision check so a route that never lands\n // is never reported.\n if (this.routeReplacementTransaction) {\n this.routeReplacementTransaction.addedRoutes.push(routeData);\n } else {\n inspectHandlerSignature(routeData.handler, routeData);\n }\n\n routes.push(routeData);\n\n if (!this.routeReplacementTransaction) {\n this.routesVersion++;\n }\n\n return this;\n }\n\n /**\n * Add a request that accepts all methods\n */\n public any(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"all\" as Route[\"method\"], path, handler, options);\n }\n\n /**\n * Add get request method\n */\n public get(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"GET\", path, handler, options);\n }\n\n /**\n * Add post request method\n */\n public post(path: string | string[], handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"POST\", path, handler, options);\n }\n\n /**\n * Add put request method\n */\n public put(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"PUT\", path, handler, options);\n }\n\n /**\n * Add delete request method\n */\n public delete(path: string | string[], handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"DELETE\", path, handler, options);\n }\n\n /**\n * Add patch request method\n */\n public patch(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"PATCH\", path, handler, options);\n }\n\n /**\n * Add head request method\n */\n public head(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"HEAD\", path, handler, options);\n }\n\n /**\n * Add options request method\n */\n public options(path: string, handler: RequestHandlerType, options: RouteOptions = {}) {\n return this.add(\"OPTIONS\", path, handler, options);\n }\n\n /**\n * Get a chainable route builder for the same route path\n */\n public route(path: string, options: RouteOptions = {}) {\n return new RouteBuilder(this, path, options);\n }\n\n /**\n * Add full restful resource routes\n * This method will generate the following routes:\n * 1. GET /path: list all resources\n * 2. GET /path/:id: get a single resource\n * 3. POST /path: create a new resource\n * 4. PUT /path/:id: update a resource\n * 5. DELETE /path/:id: delete a resource\n * 6. PATCH /path/:id: update a resource partially\n */\n public restfulResource(\n path: string,\n resource: RouteResource,\n options: RouteOptions & {\n only?: ResourceMethod[];\n except?: ResourceMethod[];\n replace?: Partial<Record<ResourceMethod, RequestHandler>> & {\n bulkDelete?: RequestHandler;\n };\n } = {},\n ) {\n // Derive the base resource name from the path before the group below\n // clears it for use as the routes' relative path. `options.name`, when\n // given, replaces the path-derived name entirely (see the explicit\n // `name` passed to `group()`) rather than composing with it.\n const baseResourceName = options.name || toCamelCase(ltrim(path, \"/\"));\n\n return this.group({ prefix: path, name: baseResourceName }, () => {\n const path = \"\";\n\n // clone the resource so we don't mess up with it\n const routeResource = resource;\n\n const isAcceptableResource = (type: ResourceMethod) => {\n return Boolean(\n // check if the route is not excluded\n (!options.except || !options.except.includes(type)) &&\n // check if the only option is set and the route is included\n (!options.only || options.only.includes(type)),\n );\n };\n\n if (routeResource.list && isAcceptableResource(\"list\")) {\n this.get(path, options.replace?.list || routeResource.list.bind(routeResource), {\n ...options,\n name: \"list\",\n restful: true,\n });\n }\n\n if (routeResource.get && isAcceptableResource(\"get\")) {\n this.get(path + \"/:id\", options.replace?.get || routeResource.get.bind(routeResource), {\n ...options,\n name: \"single\",\n restful: true,\n });\n }\n\n if (routeResource.create && isAcceptableResource(\"create\")) {\n const handler = options.replace?.create || this.manageValidation(routeResource, \"create\");\n\n this.post(path, handler, {\n ...options,\n name: \"create\",\n restful: true,\n });\n }\n\n if (routeResource.update && isAcceptableResource(\"update\")) {\n const handler = options.replace?.update || this.manageValidation(routeResource, \"update\");\n\n this.put(path + \"/:id\", handler, {\n ...options,\n name: \"update\",\n restful: true,\n });\n }\n\n if (routeResource.patch && isAcceptableResource(\"patch\")) {\n const handler = options.replace?.patch || this.manageValidation(routeResource, \"patch\");\n\n this.patch(path + \"/:id\", handler, {\n ...options,\n name: \"patch\",\n restful: true,\n });\n }\n\n if (routeResource.delete && isAcceptableResource(\"delete\")) {\n this.delete(\n path + \"/:id\",\n options.replace?.delete || routeResource.delete.bind(routeResource),\n {\n ...options,\n name: \"delete\",\n restful: true,\n },\n );\n }\n\n if (routeResource.bulkDelete && isAcceptableResource(\"delete\")) {\n this.delete(\n path,\n options.replace?.bulkDelete || routeResource.bulkDelete.bind(routeResource),\n {\n ...options,\n name: \"bulkDelete\",\n restful: true,\n },\n );\n }\n\n return this;\n });\n }\n\n /**\n * Group routes with options\n */\n public group(options: GroupedRoutesOptions, callback: RouterGroupCallback) {\n const {\n prefix,\n // name must always be defined because\n // if there are multiple groups without name\n // they might generate the same route name\n // thus causing an error\n // in this case we need always to make sure that\n // the name is always defined.\n name = prefix ? trim(prefix.replace(/\\//g, \".\"), \".\") : undefined,\n middleware,\n } = options;\n\n if (prefix) {\n this.stacks.prefix.push(prefix);\n }\n\n if (name) {\n this.stacks.name.push(name);\n }\n\n if (middleware) {\n this.stacks.middleware.push(...middleware);\n }\n\n try {\n callback();\n } finally {\n // Always pop/splice this group's stacks, even if the callback throws,\n // so a throwing group never leaks state onto the process-global singleton.\n if (prefix) {\n this.stacks.prefix.pop();\n }\n\n if (name) {\n this.stacks.name.pop();\n }\n\n if (middleware) {\n this.stacks.middleware.splice(\n this.stacks.middleware.length - middleware.length,\n middleware.length,\n );\n }\n }\n\n return this;\n }\n\n /**\n * Add prefix to all routes in the given callback\n */\n public prefix(prefix: string, callback: () => void) {\n return this.group({ prefix }, callback);\n }\n\n /**\n * Add API version prefix to all routes in the given callback\n * Automatically formats the version as /v{version}\n * @example\n * router.version(\"1\", () => {\n * router.get(\"/users\", getUsersV1); // /v1/users\n * });\n *\n * router.version(\"2\", () => {\n * router.get(\"/users\", getUsersV2); // /v2/users\n * });\n */\n public version(version: string | number, callback: () => void) {\n return this.prefix(`/v${version}`, callback);\n }\n\n /**\n * Wrap route additions with a source file path\n * Used for tracking which routes come from which file (for HMR)\n * @param sourceFile Relative path to the source file (e.g., \"src/app/users/routes.ts\")\n * @param callback Function that adds routes (will have sourceFile injected)\n */\n public async withSourceFile<T = any>(\n sourceFile: string,\n callback: () => T | Promise<T>,\n ): Promise<T | undefined> {\n // Set source file in stacks\n this.stacks.sourceFile = sourceFile;\n\n try {\n // Execute callback (routes added here will have sourceFile injected)\n return await callback();\n } catch (error) {\n // Log loudly, then RETHROW. Swallowing here silently dropped a whole\n // route file (duplicate name, invalid action, import error) so the\n // surface 404'd with no boot error. The caller decides what a throw\n // means: at boot it aborts the boot, during HMR the dev server's\n // batch-reload handler catches it, prints it, and keeps serving.\n log.error({\n module: \"router\",\n action: \"registerRoutes\",\n message: `Failed to register routes from \"${sourceFile}\"`,\n context: { sourceFile, error },\n });\n\n throw error;\n } finally {\n // Always clear source file from stacks so a throwing file never leaks\n // its sourceFile onto the process-global singleton.\n delete this.stacks.sourceFile;\n }\n }\n\n /**\n * Remove all routes that belong to a specific source file\n * Used when reloading routes files via HMR\n * @param sourceFile Relative path to the source file\n */\n public removeRoutesBySourceFile(sourceFile: string): void {\n if (this.routeReplacementTransaction) {\n this.routeReplacementTransaction.routes = this.routeReplacementTransaction.routes.filter(\n (route) => route.sourceFile !== sourceFile,\n );\n this.routeReplacementTransaction.removedSourceFiles.add(sourceFile);\n return;\n }\n\n this.routes = this.routes.filter((route) => route.sourceFile !== sourceFile);\n\n // Keep the signature diagnostics in step, so a reload that fixes a handler\n // stops reporting the version it replaced.\n forgetPositionalHandlerSuspects(sourceFile);\n\n this.routesVersion++;\n }\n\n /**\n * Atomically replace every route owned by the given exact source-file keys.\n *\n * The installer mutates an isolated draft. Public reads keep seeing the old\n * live table across awaits, and a failed installer discards every draft\n * mutation. A successful installer swaps the whole draft in one synchronous\n * commit and invalidates the dev route registry exactly once.\n */\n public async replaceRoutesBySourceFiles<T>(\n sourceFiles: Iterable<string>,\n install: () => T | Promise<T>,\n ): Promise<T> {\n if (this.routeReplacementTransaction) {\n throw new Error(\"Cannot nest route replacement transactions\");\n }\n\n const removedSourceFiles = new Set(sourceFiles);\n\n this.routeReplacementTransaction = {\n routes: this.routes.filter((route) => !removedSourceFiles.has(route.sourceFile)),\n removedSourceFiles,\n addedRoutes: [],\n };\n\n try {\n const result = await install();\n const transaction = this.routeReplacementTransaction!;\n\n this.routes = transaction.routes;\n this.routeReplacementTransaction = undefined;\n\n // Diagnostic state follows the same commit boundary as the route table.\n // Added routes were deliberately not inspected while still speculative.\n for (const sourceFile of transaction.removedSourceFiles) {\n forgetPositionalHandlerSuspects(sourceFile);\n }\n\n const committedRoutes = new Set(transaction.routes);\n\n for (const route of transaction.addedRoutes) {\n if (committedRoutes.has(route)) {\n inspectHandlerSignature(route.handler, route);\n }\n }\n\n this.routesVersion++;\n\n return result;\n } catch (error) {\n this.routeReplacementTransaction = undefined;\n throw error;\n }\n }\n\n /**\n * Manage validation system for the given resource\n */\n private manageValidation(resource: RouteResource, method: \"create\" | \"update\" | \"patch\") {\n const handler = resource[method]?.bind(resource) as RequestHandler;\n\n const methodValidation = resource?.validation?.[method];\n\n if (method === \"patch\") {\n handler.validation = methodValidation;\n\n if (handler.validation?.validate) {\n handler.validation.validate = handler.validation.validate.bind(resource);\n }\n\n if (resource.validation?.patch) {\n handler.validation = merge(resource.validation.patch, handler.validation);\n }\n\n return handler;\n }\n\n if (!resource.validation || (!methodValidation && !resource.validation.all)) return handler;\n\n if (resource.validation.all) {\n const validationMethods = {\n all: resource?.validation?.all?.validate,\n [method]: methodValidation?.validate,\n };\n\n const validation: RequestHandlerValidation = {};\n\n if (resource.validation.all.schema || methodValidation?.schema) {\n if (!methodValidation?.schema && resource.validation.all.schema) {\n // Case 2: Only all.schema exists - clone it for this method\n validation.schema = resource.validation.all.schema;\n } else if (methodValidation?.schema && resource.validation.all.schema) {\n // Case 3: Both exist - merge them (all is base, method overrides)\n validation.schema = resource.validation.all.schema.merge(methodValidation.schema);\n } else if (methodValidation?.schema && !resource.validation.all.schema) {\n // Case 1: Only method schema exists - use it directly\n validation.schema = methodValidation.schema;\n }\n // Case 4: Neither exists - handled by outer if being false\n }\n\n if (validationMethods.all || validationMethods[method]) {\n validation.validate = async ({ request, response }: HttpContext) => {\n if (validationMethods.all) {\n const output = await validationMethods.all.call(resource, { request, response });\n\n if (output) return output;\n }\n\n if (validationMethods[method]) {\n return await validationMethods[method]?.call(resource, { request, response });\n }\n\n return;\n };\n }\n\n if (!isEmpty(validation)) {\n handler.validation = validation;\n }\n } else {\n handler.validation = resource.validation[method];\n\n if (handler.validation?.validate) {\n handler.validation.validate = handler.validation.validate.bind(resource);\n }\n }\n\n return handler;\n }\n\n /**\n * Get all routes list\n */\n public list() {\n return this.routes;\n }\n\n /**\n * Number of routes currently registered on the singleton.\n *\n * Used as a readiness signal: a successful boot that ends up with zero\n * routes almost always means a route module failed to register silently\n * (see `withSourceFile`), so callers can surface that instead of letting\n * the whole surface 404.\n */\n public routeCount(): number {\n return this.routes.length;\n }\n\n /**\n * Register routes to the server\n */\n public scan(server: FastifyInstance) {\n // Every route is registered by the time we scan, so this is the first\n // moment the whole list can be reported at once — and it is still before a\n // single request is served.\n reportPositionalHandlerSuspects();\n\n this.eventListeners.beforeScan?.forEach((callback) => callback(this, server));\n\n this.routes.forEach((route) => {\n const requestMethod = route.method.toLowerCase();\n const requestMethodFunction = server[requestMethod].bind(server);\n\n const options = {\n ...route.serverOptions,\n config: {\n ...route.serverOptions?.config,\n ...(route.rateLimit && { rateLimit: route.rateLimit }),\n },\n };\n\n requestMethodFunction(\n route.path,\n options,\n async (baseRequest: FastifyRequest, reply: FastifyReply) => {\n const { output, response } = await this.handleRoute(route)(baseRequest, reply);\n\n return output || response.baseResponse;\n },\n );\n });\n\n for (const directoryOptions of this.staticDirectories) {\n server.register(fastifyStatic, {\n ...directoryOptions,\n decorateReply: false,\n });\n }\n\n this.eventListeners.afterScanning?.forEach((callback) => callback(this, server));\n }\n\n /**\n * Scan routes for the development server\n * Uses wildcard routing with find-my-way for HMR support\n */\n public scanDevServer(server: FastifyInstance) {\n // Same boot-time report as production `scan`. Routes reloaded later by HMR\n // are still recorded, but only surface on the next scan or via\n // `warlock doctor`.\n reportPositionalHandlerSuspects();\n\n this.eventListeners.beforeScan?.forEach((callback) => callback(this, server));\n\n let routeRegistry: RouteRegistry | undefined;\n let registryVersion = -1;\n\n // Rebuilt when the route table changes rather than per request. Building it\n // inside the handler re-registered every route on every hit — and `all`\n // routes expand into seven registrations each. Keyed on `routesVersion`\n // rather than on the route count, so an HMR reload that swaps a route\n // without changing the count still invalidates.\n const resolveRouteRegistry = () => {\n if (routeRegistry && registryVersion === this.routesVersion) {\n return routeRegistry;\n }\n\n routeRegistry = new RouteRegistry();\n\n routeRegistry.register(this.routes);\n\n registryVersion = this.routesVersion;\n\n return routeRegistry;\n };\n\n // Matching happens here, in Fastify's own onRequest phase, so a route's\n // `serverOptions.onRequest` still runs BEFORE body parsing. Production\n // `scan()` gets that from per-route registration; the wildcard dispatcher\n // has no per-route slot, so without this the hook silently never runs.\n server.addHook(\n \"onRequest\",\n async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = resolveRouteRegistry().find(fastifyRequest.method, fastifyRequest.url);\n\n if (!match) {\n return undefined;\n }\n\n (fastifyRequest as DevDispatchRequest).matchedDevRoute = match;\n\n fastifyRequest.params = match.params;\n\n return runRouteHooks(match.route, \"onRequest\", fastifyRequest, fastifyReply);\n },\n );\n\n for (const phase of FORWARDED_ROUTE_PHASES.filter((name) => name !== \"onRequest\")) {\n server.addHook(phase, async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = (fastifyRequest as DevDispatchRequest).matchedDevRoute;\n\n if (!match) {\n return undefined;\n }\n\n return runRouteHooks(match.route, phase, fastifyRequest, fastifyReply);\n });\n }\n\n // Shared handler for wildcard routing\n const wildcardHandler = async (fastifyRequest: FastifyRequest, fastifyReply: FastifyReply) => {\n const match = (fastifyRequest as DevDispatchRequest).matchedDevRoute;\n\n // No match found - return 404\n if (!match) {\n return fastifyReply.code(404).send({\n error: \"Route not found\",\n path: fastifyRequest.url,\n method: fastifyRequest.method,\n });\n }\n\n try {\n // Call the matched route handler\n const { output, response } = await this.handleRoute(match.route)(\n fastifyRequest,\n fastifyReply,\n );\n\n return output || response.baseResponse;\n } catch (error) {\n log.error(\"router\", \"dev-dispatch\", error as Error, {\n method: fastifyRequest.method,\n url: fastifyRequest.url,\n });\n\n throw error;\n }\n };\n\n // Register wildcard route for all methods EXCEPT OPTIONS (to avoid conflict with CORS)\n // OPTIONS is handled by @fastify/cors plugin for preflight requests\n const methods = [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"PATCH\"] as const;\n for (const method of methods) {\n server.route({\n method,\n url: \"*\",\n handler: wildcardHandler,\n });\n }\n\n // Register static directories\n for (const directoryOptions of this.staticDirectories) {\n server.register(fastifyStatic, {\n ...directoryOptions,\n decorateReply: false,\n });\n }\n\n this.eventListeners.afterScanning?.forEach((callback) => callback(this, server));\n }\n\n /**\n * Get the route path for the given route name\n */\n public getRoute(name: string, params: any = {}) {\n const route = this.routes.find((route) => route.name === name);\n\n if (!route) {\n throw new Error(`Route name \"${name}\" not found`);\n }\n\n let path = route.path;\n\n if (route.path.includes(\":\")) {\n Object.keys(params).forEach((key) => {\n path = path.replace(\":\" + key, params[key]);\n });\n }\n\n return path;\n }\n\n /**\n * Handle the given route\n */\n private handleRoute(route: Route) {\n return async (fastifyRequest: FastifyRequest, fastifyResponse: FastifyReply) => {\n const request = new Request();\n const response = new Response();\n response.setResponse(fastifyResponse);\n request.response = response;\n\n response.request = request;\n\n request.setRequest(fastifyRequest).setRoute(route);\n\n /*\n Both ends of the entry, not just the start — see\n `log-request-lifecycle.ts`. A start line on its own made a hung request\n and an instant one look identical, and the completion line is what makes\n a MISSING one legible as a hang.\n */\n const result = await logRequestLifecycle(\n {\n info: entry => log.info(entry),\n warn: entry => log.warn(entry),\n error: entry => log.error(entry),\n now: () => performance.now(),\n },\n {\n module: \"route\",\n action: describeRouteForLog(route.method, route.path),\n requestId: request.id,\n context: { request, response },\n // Read after the run: the status is decided during the request.\n statusCode: () => fastifyResponse.statusCode,\n },\n () => request.execute(),\n );\n\n return {\n output: result,\n response,\n request,\n };\n };\n }\n}\n\nexport const router = Router.getInstance();\n\ncontainer.set(\"router\", router);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.mts","names":[],"sources":["../../../../../../../core/src/socket/utils.ts"],"mappings":";;;;;AAMA;iBAAgB,eAAA,
|
|
1
|
+
{"version":3,"file":"utils.d.mts","names":[],"sources":["../../../../../../../core/src/socket/utils.ts"],"mappings":";;;;;AAMA;iBAAgB,eAAA,IAAmB,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../core/src/storage/config.ts"],"mappings":";;;;;;AAaA;iBAAgB,aAAA,
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../core/src/storage/config.ts"],"mappings":";;;;;;AAaA;iBAAgB,aAAA,IAAiB,qBAAqB;AAAA,iBACtC,aAAA,UAAuB,GAAA,WAAc,CAAC;AAAA,iBACtC,aAAA,UAAuB,GAAA,UAAa,YAAA,EAAc,CAAA,GAAI,CAAC;AAAA,cAS1D,qBAAA;mBACM,yBAAA,KAA4B,mBAAA;iBAM9B,yBAAA,GAA4B,cAAA,KAAiB,mBAAA;gBAM9C,sBAAA,GAAyB,cAAA,KAAiB,mBAAA;oBAMtC,yBAAA,GAA4B,cAAA,KAAiB,mBAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-driver-context.d.mts","names":[],"sources":["../../../../../../../../core/src/storage/context/storage-driver-context.ts"],"mappings":";;;;KAGY,yBAAA;EACV,MAAA,GAAS,qBAAA;EACT,MAAA;EACA,QAAA,GAAW,MAAM;AAAA;;;;;;;;AAAA;AACjB;;;;;;;;;;cAoBI,oBAAA,SAA6B,OAAA,CAAQ,yBAAA;EAAD;;;EAIjC,SAAA,
|
|
1
|
+
{"version":3,"file":"storage-driver-context.d.mts","names":[],"sources":["../../../../../../../../core/src/storage/context/storage-driver-context.ts"],"mappings":";;;;KAGY,yBAAA;EACV,MAAA,GAAS,qBAAA;EACT,MAAA;EACA,QAAA,GAAW,MAAM;AAAA;;;;;;;;AAAA;AACjB;;;;;;;;;;cAoBI,oBAAA,SAA6B,OAAA,CAAQ,yBAAA;EAAD;;;EAIjC,SAAA,IAAa,qBAAA;EAAA;;;EAOb,SAAA;EAiBA;;;EAVA,WAAA,IAAe,MAAA;EAYS;;;;;;EAFxB,SAAA,CACL,MAAA,EAAQ,qBAAA,EACR,OAAA;IAAY,MAAA;IAAiB,QAAA,GAAW,MAAA;EAAA;EAwC/B;;;;AAAiD;;;;EAvBrD,SAAA,CAAU,MAAA;;;;EAOV,WAAA;;;;EAOA,UAAA,IAAc,yBAAA;AAAA;AAAA,cASV,oBAAA,EAAoB,oBAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-driver.d.mts","names":[],"sources":["../../../../../../../../core/src/storage/drivers/cloud-driver.ts"],"mappings":";;;;;;AA6DA;;;;AAAuC;AAuFvC;;iBAvFsB,MAAA,
|
|
1
|
+
{"version":3,"file":"cloud-driver.d.mts","names":[],"sources":["../../../../../../../../core/src/storage/drivers/cloud-driver.ts"],"mappings":";;;;;;AA6DA;;;;AAAuC;AAuFvC;;iBAvFsB,MAAA,IAAU,OAAO;;;;;;;;;;;;;;;;;uBAuFjB,WAAA,kBACH,OAAA,CAAQ,yBAAA,IAA6B,yBAAA,aAC3C,0BAAA;EAgBe,OAAA,EAAS,QAAA;EAiPhC;;;EAAA,UA7PO,MAAA,EAAS,YAAA,qCAAiD,QAAA;EAoU1B;;;EAAA,UA/ThC,WAAA;IACR,UAAA;IACA,cAAA;IACA,UAAA;IACA,iBAAA;EAAA;cAGwB,OAAA,EAAS,QAAA;EAkhBhC;;;EAAA,kBAjfsB,IAAA,EAAM,iBAAA;EAwjBsB;;;;EAAA,SAljBrC,GAAA,CAAI,QAAA;EAomBiD;;;;EAAA,UA9lB3D,WAAA;EA8sB4C;;;;;;;;;EA7rB/C,WAAA,CAAY,QAAA;EA9ER;;;;EAAA,UAsGD,aAAA,CAAc,IAAA;EAlG4C;;;;;;;;;;EAAA,UAmHpD,SAAA,IACd,SAAA,QAAiB,OAAA,CAAQ,CAAA,GACzB,aAAA,YACC,OAAA,CAAQ,CAAA;EAnEK;;;;;;;;;;;;;;;;;;EAAA,UAqHN,gBAAA,CAAiB,KAAA;EAoCzB;;;EADW,GAAA,CACX,IAAA,EAAM,MAAA,EACN,QAAA,UACA,OAAA,GAAU,UAAA,GACT,OAAA,CAAQ,oBAAA;EAAR;;;;EA0CU,SAAA,CACX,MAAA,EAAQ,QAAA,EACR,QAAA,UACA,OAAA,GAAU,UAAA,GACT,OAAA,CAAQ,oBAAA;EAFT;;;EAgDW,GAAA,CAAI,QAAA,WAAmB,OAAA,CAAQ,MAAA;EA9CjC;;;EAuEE,SAAA,CAAU,QAAA,WAAmB,OAAA,CAAQ,QAAA;EAzBN;;;EAkD/B,MAAA,CAAO,QAAA,WAAmB,OAAA;EAzBW;;;EA8CrC,UAAA,CAAW,SAAA,aAAsB,OAAA,CAAQ,gBAAA;EAAzC;;;;;;;;;EAqDA,eAAA,CAAgB,aAAA,WAAwB,OAAA;EAuFxC;;;EA3BA,MAAA,CAAO,QAAA,WAAmB,OAAA;EAkC1B;;;EAPA,YAAA,CAAa,QAAA,UAAkB,SAAA,YAAmB,OAAA;EAOa;;;EAA/D,eAAA,CAAgB,QAAA,UAAkB,OAAA,GAAU,gBAAA,GAAmB,OAAA;EAwB1E;;;EAFW,qBAAA,CACX,QAAA,UACA,OAAA,GAAU,sBAAA,GACT,OAAA;EA4BsC;;;EAA5B,QAAA,CAAS,QAAA,WAAmB,OAAA,CAAQ,eAAA;EA+BZ;;;EAAxB,IAAA,CAAK,QAAA,WAAmB,OAAA;EAYQ;;;EAAhC,IAAA,CAAK,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,oBAAA;EAyCrB;;;EAAnB,IAAA,CAAK,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,oBAAA;EASnC;;;EAAL,IAAA,CAAK,SAAA,UAAmB,OAAA,GAAU,WAAA,GAAc,OAAA,CAAQ,eAAA;EAAA;;;EAwD9D,SAAA;EAcsB;;;EAPtB,SAAA;EA6BoB;;;EAtBd,eAAA,CAAgB,QAAA,UAAkB,YAAA,WAAuB,OAAA;EA0CzD;;;EApBA,aAAA,CAAc,QAAA,UAAkB,UAAA,EAAY,cAAA,GAAiB,OAAA;EAoDhE;;;EAhCG,aAAA,CAAc,QAAA,WAAmB,OAAA,CAAQ,cAAA;EAuC9B;;AAAgB;EAAhB,UAPd,aAAA,CAAc,MAAA,EAAQ,MAAA;;;;YAOtB,aAAA,CAAc,QAAA;AAAA"}
|