@afora/mcp 0.0.3 → 0.0.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/dist/bin.js +2 -2
- package/dist/guide.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -224,7 +224,7 @@ a file of your own.
|
|
|
224
224
|
|
|
225
225
|
- \`exceptions\`: \`{ layer, message? }\`; \`layer\` is one of \`exceptionLayers\` of \`afora_catalog\`; the message is "<Name>." when omitted.
|
|
226
226
|
- \`useCases\`: \`input\`, \`response\` and \`context\` (optional) are a blueprint name or \`{ "fields": {...} }\`. \`dependencies\` map a key to a use case, repository
|
|
227
|
-
or environment. \`errors\` are declared exceptions or exceptions exported by @afora/core (\`coreExceptions\` of \`afora_catalog\`).
|
|
227
|
+
or environment, or to \`"Logger"\` for the logger of the application (an \`ILogger\`; no step reads it). \`errors\` are declared exceptions or exceptions exported by @afora/core (\`coreExceptions\` of \`afora_catalog\`).
|
|
228
228
|
- \`implementation\` is \`{ "steps": [...] }\` (generated complete, no hand-written file) or \`{ "import": "../handlers/x.ts#Name" }\`:
|
|
229
229
|
the generated file has \`abstract class <Name>Base\`; write \`class Name extends <Name>Base\` with \`execute\` at that path.
|
|
230
230
|
A use case cannot list an imported (hand-written) use case as a dependency.
|
|
@@ -665,7 +665,7 @@ var tools = [
|
|
|
665
665
|
description: "PascalCase name, unique across all kinds."
|
|
666
666
|
},
|
|
667
667
|
declaration: {
|
|
668
|
-
description: 'The declaration. property: { base, with? } | { enum } | { opaque, import }. blueprint/environment: { fields }. record/entity: { fields, unique?, sensitive?, change?, plugins? }. repository: { entity, actions, provider?, deletion?: hard | soft, relations?, environments? }. exception: { layer, message? }. useCase: { description?, tags?, deprecated?, input, response, context?, dependencies?, errors?, implementation: { steps: [...] } | { import: "../handlers/x.ts#Name" } } (see the guide for steps). command: { description?, tags?, context?, useCases: { op: "UseCaseName" }, transaction?, events?: "after-commit" | "manual" | { import }, onEventError?: { import } }. output: { commands: [names], statuses?: { ExceptionName: 400-599 }, pipe: [{ transform, options? } | { sink, options? } | { import }] }. provider: "inMemory" | { use, options? } | { import: "./file.ts#name" } (the application provider is the top-level `provider` of the spec: afora_spec_write).',
|
|
668
|
+
description: 'The declaration. property: { base, with? } | { enum } | { opaque, import }. blueprint/environment: { fields }. record/entity: { fields, unique?, sensitive?, change?, plugins? }. repository: { entity, actions, provider?, deletion?: hard | soft, relations?, environments? }. exception: { layer, message? }. useCase: { description?, tags?, deprecated?, input, response, context?, dependencies?, errors?, implementation: { steps: [...] } | { import: "../handlers/x.ts#Name" } } (see the guide for steps). command: { description?, tags?, context?, useCases: { op: "UseCaseName" }, transaction?, events?: "after-commit" | "manual" | { import }, onEventError?: { import } }. output: { commands: [names], statuses?: { ExceptionName: 400-599 }, pipe: [{ transform, options? } | { sink, options? } | { import }] }. provider: "inMemory" | { use, options? } | { import: "./file.ts#name" } (the application provider is the top-level `provider` of the spec: afora_spec_write). A use case dependency can also be "Logger"; the top-level `log` is { level?, providers: ["json" | "console" | { import }] }.',
|
|
669
669
|
anyOf: Object.values(declarationSchemas).map((schema) => json(schema))
|
|
670
670
|
}
|
|
671
671
|
},
|
package/dist/guide.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** A short guide to the afora.json format, for the agent (the resource afora://spec/guide). */
|
|
2
|
-
export declare const GUIDE = "# afora.json guide\n\n`afora.json` describes a domain. `afora_generate` turns it into TypeScript on `@afora/core`\n(one file per kind of declaration, under `output.dir`, default `src/domain`). The generated files\nstart with \"// Generated by @afora/codegen from afora.json. Do not edit.\" and are overwritten on\nevery run, so change the JSON, never those files. To add behavior, extend the generated class in\na file of your own.\n\n## Workflow\n\n1. `afora_catalog`: what you can use (Std properties, record plugins, rules).\n2. `afora_spec_read`: the current spec (or the one extracted from the code).\n3. Edit with `afora_spec_upsert` / `afora_spec_remove` (one declaration at a time; the whole\n spec must stay valid, so declare what a declaration uses first) or `afora_spec_write`.\n4. `afora_spec_validate`, then `afora_generate` (`dryRun: true` to preview). The result has the\n TypeScript diagnostics of the generated files: fix the spec until there are none.\n\n## Shape\n\n```json\n{\n \"version\": 1,\n \"name\": \"blog\",\n \"output\": { \"dir\": \"src/domain\" },\n \"provider\": \"inMemory\",\n \"properties\": { \"Age\": { \"base\": \"integer\", \"with\": { \"min\": 0, \"max\": 150 } },\n \"Status\": { \"enum\": [\"draft\", \"published\"] } },\n \"blueprints\": { \"Address\": { \"fields\": { \"street\": \"string\", \"zip\": \"string\" } } },\n \"records\": { \"Money\": { \"fields\": { \"amount\": \"number\", \"currency\": \"string\" } } },\n \"entities\": { \"User\": { \"fields\": { \"name\": \"text\", \"email\": \"email\", \"password\": \"password\",\n \"age\": \"Age?\", \"address\": \"Address\", \"tags\": \"string[]\" },\n \"unique\": [\"email\"], \"change\": [\"name\"] },\n \"Post\": { \"fields\": { \"title\": \"text\", \"status\": \"Status\",\n \"author\": \"User\", \"coauthors\": \"User[]\" } } },\n \"environments\": { \"Database\": { \"fields\": { \"DATABASE_URL\": \"url\", \"PORT\": \"integer\" } } },\n \"repositories\": { \"Users\": { \"entity\": \"User\", \"actions\": [\"findById\", \"findByEmail\", \"create\", \"update\"],\n \"environments\": [\"Database\"] },\n \"Posts\": { \"entity\": \"Post\", \"actions\": [\"findById\", \"findManyByAuthor\", \"create\"],\n \"relations\": [\"Users\"] } }\n}\n```\n\n## Fields\n\n- Shorthand string: `\"email\"`, `\"Age?\"` (optional), `\"string[]\"` (list), `\"User[]?\"` (optional list).\n- Lowercase names are Std properties (see `afora_catalog`: id, slug, email, phone, url, ip, password,\n string, text, number, integer, boolean, date, dateTime...). PascalCase names are declarations of\n the spec (properties, blueprints, records, entities).\n- Object form: `{ \"type\": \"string\", \"optional\": true, \"list\": { \"minItems\": 1, \"maxItems\": 5 }, \"with\": { \"pattern\": \"^a\" } }`.\n `with` applies to each item of a list and only takes options valid for the type (min/max/pattern for strings,\n min/max/exclusiveMin/exclusiveMax/multipleOf for numbers, plus title/description/examples/deprecated/readOnly/writeOnly/default).\n- Enum: `{ \"enum\": [\"a\", \"b\"] }`. Nested object in place: `{ \"fields\": { \"lat\": \"number\", \"lng\": \"number\" } }`.\n- A property of your own: `{ \"base\": \"integer\", \"with\": {...} }`, `{ \"enum\": [...] }`, or\n `{ \"opaque\": true, \"import\": \"./my-property\" }` for a hand-written one (imported as it is).\n\n## Rules the validator enforces\n\n- Declaration names are PascalCase and unique across every kind; names exported by @afora/core are taken.\n- No circular references between declarations (store an id instead of the entity).\n- An entity cannot declare `id`, `createdAt`, `updatedAt`, `deletedAt` (it gets them). No record member names\n (`toJSON`, `set`, `onSet`...); see `reservedRecordKeys` in the catalog. `blueprint` and `options` are valid fields.\n- `unique`, `sensitive`, `change` must name fields (or keys of its plugins); `change` cannot name an identity key.\n `password` is always sensitive and `slug` always unique; `id` is always unique.\n- `plugins`: Std record plugins (publishable, archivable, taggable, ownable, authenticatable); their keys cannot be declared again.\n- Repository actions: `findById`; `findBy<Key>` only for unique keys (id, `unique`, slug fields, unique plugin keys such as\n `email` of authenticatable); `findManyBy<Key>` only for keys that are not unique; `create`, `update`, `delete`, `count`, and the\n queries `findMany`, `exists`, `findPage` (no key needed; `where`, `orderBy`, `limit`, `offset` at runtime).\n- `provider` (top level for the application, and optionally on a repository, which then uses its own): `\"inMemory\"`,\n `{ \"use\": \"<name>\", \"options\": {...} }` with a name from the `providers` of `afora_catalog` (external ones are given\n to the server with `--providers`), or `{ \"import\": \"./db.ts#provider\" }` for one created by hand. Options are JSON or\n `{ \"import\": \"./db.ts#db\" }` references (any depth); imports are relative to `output.dir` and never run by the validator.\n Omitting `provider` is not the same as `\"inMemory\"`: a repository without one uses the application's. For a provider\n not created with `providerOf`, `afora_extract` needs `imports` (`provider` / `repositories.<Name>.provider`).\n- Repository `deletion`: `\"hard\"` (default) physically removes rows; `\"soft\"` retains `deletedAt` and hides deleted rows from reads, counts and relations.\n- `relations` must list exactly one repository for each entity the repository's entity holds, at any depth (inside\n records, blueprints and nested objects), not through another entity.\n- Environments hold properties only (no lists, no objects).\n- `extends`: other specs (relative path to their afora.json, or a package) whose declarations you can reference;\n the same name for different declarations is a conflict.\n\n## Exceptions and use cases\n\n```json\n{\n \"exceptions\": { \"UserAlreadyExists\": { \"layer\": \"use-case\", \"message\": \"A user with this email already exists.\" } },\n \"useCases\": {\n \"Register\": {\n \"description\": \"Signs a user up.\", \"tags\": [\"users\"],\n \"input\": { \"fields\": { \"name\": \"text\", \"email\": \"email\", \"plainPassword\": \"string\" } },\n \"response\": { \"fields\": { \"user\": \"User\" } },\n \"dependencies\": { \"users\": \"Users\" },\n \"errors\": [\"UserAlreadyExists\", \"RepositoryUniqueViolation\"],\n \"implementation\": { \"steps\": [\n { \"find\": \"users.findByEmail\", \"args\": [\"$input.email\"], \"as\": \"existing\" },\n { \"ensure\": \"absent\", \"value\": \"$existing\", \"error\": \"UserAlreadyExists\" },\n { \"run\": \"hashPassword\", \"fn\": { \"import\": \"../hash#hashPassword\" }, \"args\": [\"$input.plainPassword\"], \"as\": \"hash\" },\n { \"build\": \"User\", \"values\": { \"name\": \"$input.name\", \"email\": \"$input.email\", \"password\": \"$hash\" }, \"as\": \"user\" },\n { \"create\": \"users\", \"value\": \"$user\", \"as\": \"saved\" },\n { \"return\": { \"user\": \"$saved\" } }\n ] }\n }\n }\n}\n```\n\n- `exceptions`: `{ layer, message? }`; `layer` is one of `exceptionLayers` of `afora_catalog`; the message is \"<Name>.\" when omitted.\n- `useCases`: `input`, `response` and `context` (optional) are a blueprint name or `{ \"fields\": {...} }`. `dependencies` map a key to a use case, repository\n or environment. `errors` are declared exceptions or exceptions exported by @afora/core (`coreExceptions` of `afora_catalog`).\n- `implementation` is `{ \"steps\": [...] }` (generated complete, no hand-written file) or `{ \"import\": \"../handlers/x.ts#Name\" }`:\n the generated file has `abstract class <Name>Base`; write `class Name extends <Name>Base` with `execute` at that path.\n A use case cannot list an imported (hand-written) use case as a dependency.\n- Steps run in order and `return` is last, exactly once: `find` (\"<repo>.<read action>\"), `ensure` (exists | absent | equals | notEquals | true | false;\n `other` for the comparisons, `error` a declared exception name or a core one, listed in `errors`; optional `details`), `build` (a record or\n entity name, `values`, `as`), `change` ($ref, `key` in the class's change keys, `value`), `invoke` ($ref, `method`, `args`),\n `create` / `update` / `delete` (repository key, `value`), `call` (use case key, `input`) and `run` (a unique name, `fn` is\n `{ \"import\": \"../x.ts#fn\" }`, `args`; the function gets (...args, { context })). `as` binds the result.\n- Values: `\"$input.key\"`, `\"$context.key\"`, `\"$env.<dependency>.VAR\"`, `\"$name.path.0\"` (a bound result); a literal; `{ \"literal\": \"$5\" }` for a string that\n starts with $; or an object/array of values. No expressions: compute with `run`.\n- `afora_extract` needs `imports` for `useCases.<Name>` (hand-written execute) and `useCases.<Name>.run.<name>` (run functions).\n\n## Commands and outputs\n\n```json\n{\n \"commands\": {\n \"Users\": {\n \"description\": \"Accounts.\", \"tags\": [\"public\"],\n \"context\": { \"fields\": { \"userId\": \"id?\" } },\n \"useCases\": { \"register\": \"Register\" },\n \"transaction\": true\n }\n },\n \"outputs\": {\n \"Api\": {\n \"commands\": [\"Users\"],\n \"statuses\": { \"UserAlreadyExists\": 409 },\n \"pipe\": [\n { \"transform\": \"select\", \"options\": { \"tags\": [\"public\"] } },\n { \"sink\": \"openapi\", \"options\": { \"info\": { \"title\": \"Blog\", \"version\": \"1.0.0\" } } }\n ]\n }\n }\n}\n```\n\n- `commands`: `useCases` maps an operation name (an identifier) to a declared use case; `context` is a blueprint name or `{ \"fields\": {...} }`.\n `transaction: true` runs each call in a transaction of the repositories' provider: it is checked when the code runs, not by the validator\n (the provider is unknown then). `events` is `\"after-commit\"` (default), `\"manual\"` or `{ \"import\": \"./events.ts#dispatch\" }` (a handler);\n `onEventError` is an import and only with `after-commit`. Defaults (`transaction: false`, `events: \"after-commit\"`, no tags) are not written.\n- `outputs`: `commands` are declared commands; `statuses` maps an exception name (declared or exported by @afora/core) to a status from 400 to 599;\n `pipe` is transforms and, last, at most one sink, each `{ \"transform\" | \"sink\": name, \"options\"?: {...JSON} }` with a name from the `stages` of\n `afora_catalog` (`select`, `openapi`, plus the ones given to the server with `--stages`), or `{ \"import\": \"./stages.ts#auth\" }` for a stage written by hand.\n- `afora_extract` needs `imports` for `commands.<Name>.events`, `commands.<Name>.onEventError` and `outputs.<Name>.pipe.<index>` (hand-written ones).\n";
|
|
2
|
+
export declare const GUIDE = "# afora.json guide\n\n`afora.json` describes a domain. `afora_generate` turns it into TypeScript on `@afora/core`\n(one file per kind of declaration, under `output.dir`, default `src/domain`). The generated files\nstart with \"// Generated by @afora/codegen from afora.json. Do not edit.\" and are overwritten on\nevery run, so change the JSON, never those files. To add behavior, extend the generated class in\na file of your own.\n\n## Workflow\n\n1. `afora_catalog`: what you can use (Std properties, record plugins, rules).\n2. `afora_spec_read`: the current spec (or the one extracted from the code).\n3. Edit with `afora_spec_upsert` / `afora_spec_remove` (one declaration at a time; the whole\n spec must stay valid, so declare what a declaration uses first) or `afora_spec_write`.\n4. `afora_spec_validate`, then `afora_generate` (`dryRun: true` to preview). The result has the\n TypeScript diagnostics of the generated files: fix the spec until there are none.\n\n## Shape\n\n```json\n{\n \"version\": 1,\n \"name\": \"blog\",\n \"output\": { \"dir\": \"src/domain\" },\n \"provider\": \"inMemory\",\n \"properties\": { \"Age\": { \"base\": \"integer\", \"with\": { \"min\": 0, \"max\": 150 } },\n \"Status\": { \"enum\": [\"draft\", \"published\"] } },\n \"blueprints\": { \"Address\": { \"fields\": { \"street\": \"string\", \"zip\": \"string\" } } },\n \"records\": { \"Money\": { \"fields\": { \"amount\": \"number\", \"currency\": \"string\" } } },\n \"entities\": { \"User\": { \"fields\": { \"name\": \"text\", \"email\": \"email\", \"password\": \"password\",\n \"age\": \"Age?\", \"address\": \"Address\", \"tags\": \"string[]\" },\n \"unique\": [\"email\"], \"change\": [\"name\"] },\n \"Post\": { \"fields\": { \"title\": \"text\", \"status\": \"Status\",\n \"author\": \"User\", \"coauthors\": \"User[]\" } } },\n \"environments\": { \"Database\": { \"fields\": { \"DATABASE_URL\": \"url\", \"PORT\": \"integer\" } } },\n \"repositories\": { \"Users\": { \"entity\": \"User\", \"actions\": [\"findById\", \"findByEmail\", \"create\", \"update\"],\n \"environments\": [\"Database\"] },\n \"Posts\": { \"entity\": \"Post\", \"actions\": [\"findById\", \"findManyByAuthor\", \"create\"],\n \"relations\": [\"Users\"] } }\n}\n```\n\n## Fields\n\n- Shorthand string: `\"email\"`, `\"Age?\"` (optional), `\"string[]\"` (list), `\"User[]?\"` (optional list).\n- Lowercase names are Std properties (see `afora_catalog`: id, slug, email, phone, url, ip, password,\n string, text, number, integer, boolean, date, dateTime...). PascalCase names are declarations of\n the spec (properties, blueprints, records, entities).\n- Object form: `{ \"type\": \"string\", \"optional\": true, \"list\": { \"minItems\": 1, \"maxItems\": 5 }, \"with\": { \"pattern\": \"^a\" } }`.\n `with` applies to each item of a list and only takes options valid for the type (min/max/pattern for strings,\n min/max/exclusiveMin/exclusiveMax/multipleOf for numbers, plus title/description/examples/deprecated/readOnly/writeOnly/default).\n- Enum: `{ \"enum\": [\"a\", \"b\"] }`. Nested object in place: `{ \"fields\": { \"lat\": \"number\", \"lng\": \"number\" } }`.\n- A property of your own: `{ \"base\": \"integer\", \"with\": {...} }`, `{ \"enum\": [...] }`, or\n `{ \"opaque\": true, \"import\": \"./my-property\" }` for a hand-written one (imported as it is).\n\n## Rules the validator enforces\n\n- Declaration names are PascalCase and unique across every kind; names exported by @afora/core are taken.\n- No circular references between declarations (store an id instead of the entity).\n- An entity cannot declare `id`, `createdAt`, `updatedAt`, `deletedAt` (it gets them). No record member names\n (`toJSON`, `set`, `onSet`...); see `reservedRecordKeys` in the catalog. `blueprint` and `options` are valid fields.\n- `unique`, `sensitive`, `change` must name fields (or keys of its plugins); `change` cannot name an identity key.\n `password` is always sensitive and `slug` always unique; `id` is always unique.\n- `plugins`: Std record plugins (publishable, archivable, taggable, ownable, authenticatable); their keys cannot be declared again.\n- Repository actions: `findById`; `findBy<Key>` only for unique keys (id, `unique`, slug fields, unique plugin keys such as\n `email` of authenticatable); `findManyBy<Key>` only for keys that are not unique; `create`, `update`, `delete`, `count`, and the\n queries `findMany`, `exists`, `findPage` (no key needed; `where`, `orderBy`, `limit`, `offset` at runtime).\n- `provider` (top level for the application, and optionally on a repository, which then uses its own): `\"inMemory\"`,\n `{ \"use\": \"<name>\", \"options\": {...} }` with a name from the `providers` of `afora_catalog` (external ones are given\n to the server with `--providers`), or `{ \"import\": \"./db.ts#provider\" }` for one created by hand. Options are JSON or\n `{ \"import\": \"./db.ts#db\" }` references (any depth); imports are relative to `output.dir` and never run by the validator.\n Omitting `provider` is not the same as `\"inMemory\"`: a repository without one uses the application's. For a provider\n not created with `providerOf`, `afora_extract` needs `imports` (`provider` / `repositories.<Name>.provider`).\n- Repository `deletion`: `\"hard\"` (default) physically removes rows; `\"soft\"` retains `deletedAt` and hides deleted rows from reads, counts and relations.\n- `relations` must list exactly one repository for each entity the repository's entity holds, at any depth (inside\n records, blueprints and nested objects), not through another entity.\n- Environments hold properties only (no lists, no objects).\n- `extends`: other specs (relative path to their afora.json, or a package) whose declarations you can reference;\n the same name for different declarations is a conflict.\n\n## Exceptions and use cases\n\n```json\n{\n \"exceptions\": { \"UserAlreadyExists\": { \"layer\": \"use-case\", \"message\": \"A user with this email already exists.\" } },\n \"useCases\": {\n \"Register\": {\n \"description\": \"Signs a user up.\", \"tags\": [\"users\"],\n \"input\": { \"fields\": { \"name\": \"text\", \"email\": \"email\", \"plainPassword\": \"string\" } },\n \"response\": { \"fields\": { \"user\": \"User\" } },\n \"dependencies\": { \"users\": \"Users\" },\n \"errors\": [\"UserAlreadyExists\", \"RepositoryUniqueViolation\"],\n \"implementation\": { \"steps\": [\n { \"find\": \"users.findByEmail\", \"args\": [\"$input.email\"], \"as\": \"existing\" },\n { \"ensure\": \"absent\", \"value\": \"$existing\", \"error\": \"UserAlreadyExists\" },\n { \"run\": \"hashPassword\", \"fn\": { \"import\": \"../hash#hashPassword\" }, \"args\": [\"$input.plainPassword\"], \"as\": \"hash\" },\n { \"build\": \"User\", \"values\": { \"name\": \"$input.name\", \"email\": \"$input.email\", \"password\": \"$hash\" }, \"as\": \"user\" },\n { \"create\": \"users\", \"value\": \"$user\", \"as\": \"saved\" },\n { \"return\": { \"user\": \"$saved\" } }\n ] }\n }\n }\n}\n```\n\n- `exceptions`: `{ layer, message? }`; `layer` is one of `exceptionLayers` of `afora_catalog`; the message is \"<Name>.\" when omitted.\n- `useCases`: `input`, `response` and `context` (optional) are a blueprint name or `{ \"fields\": {...} }`. `dependencies` map a key to a use case, repository\n or environment, or to `\"Logger\"` for the logger of the application (an `ILogger`; no step reads it). `errors` are declared exceptions or exceptions exported by @afora/core (`coreExceptions` of `afora_catalog`).\n- `implementation` is `{ \"steps\": [...] }` (generated complete, no hand-written file) or `{ \"import\": \"../handlers/x.ts#Name\" }`:\n the generated file has `abstract class <Name>Base`; write `class Name extends <Name>Base` with `execute` at that path.\n A use case cannot list an imported (hand-written) use case as a dependency.\n- Steps run in order and `return` is last, exactly once: `find` (\"<repo>.<read action>\"), `ensure` (exists | absent | equals | notEquals | true | false;\n `other` for the comparisons, `error` a declared exception name or a core one, listed in `errors`; optional `details`), `build` (a record or\n entity name, `values`, `as`), `change` ($ref, `key` in the class's change keys, `value`), `invoke` ($ref, `method`, `args`),\n `create` / `update` / `delete` (repository key, `value`), `call` (use case key, `input`) and `run` (a unique name, `fn` is\n `{ \"import\": \"../x.ts#fn\" }`, `args`; the function gets (...args, { context })). `as` binds the result.\n- Values: `\"$input.key\"`, `\"$context.key\"`, `\"$env.<dependency>.VAR\"`, `\"$name.path.0\"` (a bound result); a literal; `{ \"literal\": \"$5\" }` for a string that\n starts with $; or an object/array of values. No expressions: compute with `run`.\n- `afora_extract` needs `imports` for `useCases.<Name>` (hand-written execute) and `useCases.<Name>.run.<name>` (run functions).\n\n## Commands and outputs\n\n```json\n{\n \"commands\": {\n \"Users\": {\n \"description\": \"Accounts.\", \"tags\": [\"public\"],\n \"context\": { \"fields\": { \"userId\": \"id?\" } },\n \"useCases\": { \"register\": \"Register\" },\n \"transaction\": true\n }\n },\n \"outputs\": {\n \"Api\": {\n \"commands\": [\"Users\"],\n \"statuses\": { \"UserAlreadyExists\": 409 },\n \"pipe\": [\n { \"transform\": \"select\", \"options\": { \"tags\": [\"public\"] } },\n { \"sink\": \"openapi\", \"options\": { \"info\": { \"title\": \"Blog\", \"version\": \"1.0.0\" } } }\n ]\n }\n }\n}\n```\n\n- `commands`: `useCases` maps an operation name (an identifier) to a declared use case; `context` is a blueprint name or `{ \"fields\": {...} }`.\n `transaction: true` runs each call in a transaction of the repositories' provider: it is checked when the code runs, not by the validator\n (the provider is unknown then). `events` is `\"after-commit\"` (default), `\"manual\"` or `{ \"import\": \"./events.ts#dispatch\" }` (a handler);\n `onEventError` is an import and only with `after-commit`. Defaults (`transaction: false`, `events: \"after-commit\"`, no tags) are not written.\n- `outputs`: `commands` are declared commands; `statuses` maps an exception name (declared or exported by @afora/core) to a status from 400 to 599;\n `pipe` is transforms and, last, at most one sink, each `{ \"transform\" | \"sink\": name, \"options\"?: {...JSON} }` with a name from the `stages` of\n `afora_catalog` (`select`, `openapi`, plus the ones given to the server with `--stages`), or `{ \"import\": \"./stages.ts#auth\" }` for a stage written by hand.\n- `afora_extract` needs `imports` for `commands.<Name>.events`, `commands.<Name>.onEventError` and `outputs.<Name>.pipe.<index>` (hand-written ones).\n";
|
package/dist/index.js
CHANGED
|
@@ -106,7 +106,7 @@ a file of your own.
|
|
|
106
106
|
|
|
107
107
|
- \`exceptions\`: \`{ layer, message? }\`; \`layer\` is one of \`exceptionLayers\` of \`afora_catalog\`; the message is "<Name>." when omitted.
|
|
108
108
|
- \`useCases\`: \`input\`, \`response\` and \`context\` (optional) are a blueprint name or \`{ "fields": {...} }\`. \`dependencies\` map a key to a use case, repository
|
|
109
|
-
or environment. \`errors\` are declared exceptions or exceptions exported by @afora/core (\`coreExceptions\` of \`afora_catalog\`).
|
|
109
|
+
or environment, or to \`"Logger"\` for the logger of the application (an \`ILogger\`; no step reads it). \`errors\` are declared exceptions or exceptions exported by @afora/core (\`coreExceptions\` of \`afora_catalog\`).
|
|
110
110
|
- \`implementation\` is \`{ "steps": [...] }\` (generated complete, no hand-written file) or \`{ "import": "../handlers/x.ts#Name" }\`:
|
|
111
111
|
the generated file has \`abstract class <Name>Base\`; write \`class Name extends <Name>Base\` with \`execute\` at that path.
|
|
112
112
|
A use case cannot list an imported (hand-written) use case as a dependency.
|
|
@@ -655,7 +655,7 @@ var tools = [
|
|
|
655
655
|
description: "PascalCase name, unique across all kinds."
|
|
656
656
|
},
|
|
657
657
|
declaration: {
|
|
658
|
-
description: 'The declaration. property: { base, with? } | { enum } | { opaque, import }. blueprint/environment: { fields }. record/entity: { fields, unique?, sensitive?, change?, plugins? }. repository: { entity, actions, provider?, deletion?: hard | soft, relations?, environments? }. exception: { layer, message? }. useCase: { description?, tags?, deprecated?, input, response, context?, dependencies?, errors?, implementation: { steps: [...] } | { import: "../handlers/x.ts#Name" } } (see the guide for steps). command: { description?, tags?, context?, useCases: { op: "UseCaseName" }, transaction?, events?: "after-commit" | "manual" | { import }, onEventError?: { import } }. output: { commands: [names], statuses?: { ExceptionName: 400-599 }, pipe: [{ transform, options? } | { sink, options? } | { import }] }. provider: "inMemory" | { use, options? } | { import: "./file.ts#name" } (the application provider is the top-level `provider` of the spec: afora_spec_write).',
|
|
658
|
+
description: 'The declaration. property: { base, with? } | { enum } | { opaque, import }. blueprint/environment: { fields }. record/entity: { fields, unique?, sensitive?, change?, plugins? }. repository: { entity, actions, provider?, deletion?: hard | soft, relations?, environments? }. exception: { layer, message? }. useCase: { description?, tags?, deprecated?, input, response, context?, dependencies?, errors?, implementation: { steps: [...] } | { import: "../handlers/x.ts#Name" } } (see the guide for steps). command: { description?, tags?, context?, useCases: { op: "UseCaseName" }, transaction?, events?: "after-commit" | "manual" | { import }, onEventError?: { import } }. output: { commands: [names], statuses?: { ExceptionName: 400-599 }, pipe: [{ transform, options? } | { sink, options? } | { import }] }. provider: "inMemory" | { use, options? } | { import: "./file.ts#name" } (the application provider is the top-level `provider` of the spec: afora_spec_write). A use case dependency can also be "Logger"; the top-level `log` is { level?, providers: ["json" | "console" | { import }] }.',
|
|
659
659
|
anyOf: Object.values(declarationSchemas).map((schema) => json(schema))
|
|
660
660
|
}
|
|
661
661
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afora/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "An MCP server that lets an agent discover the afora catalog, edit afora.json, validate it, generate the code and check the result.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"afora",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"test": "bun test"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@afora/codegen": "^0.0.
|
|
52
|
+
"@afora/codegen": "^0.0.4",
|
|
53
53
|
"@modelcontextprotocol/sdk": "^1.32.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|