@adula/kit 0.2.0-alpha.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +12 -2
  2. package/build/agent/capabilities.md +49 -7
  3. package/build/agent/skills/idea-review/SKILL.md +26 -1
  4. package/build/agent/skills/module-review/SKILL.md +22 -0
  5. package/build/agent/skills/perf-review/SKILL.md +24 -0
  6. package/build/agent/skills/schema-review/SKILL.md +22 -0
  7. package/build/agent/skills/security-review/SKILL.md +24 -0
  8. package/build/agent/skills/ui-review/SKILL.md +22 -0
  9. package/build/commands/capabilities.d.ts +4 -0
  10. package/build/commands/capabilities.js +35 -4
  11. package/build/commands/main.d.ts +4 -2
  12. package/build/database/migrations/1770000000004_kit_collaboration.d.ts +5 -0
  13. package/build/database/migrations/1770000000004_kit_collaboration.js +10 -0
  14. package/build/database/migrations/1770000000005_kit_assignments.d.ts +5 -0
  15. package/build/database/migrations/1770000000005_kit_assignments.js +10 -0
  16. package/build/database/migrations/1770000000006_kit_messaging.d.ts +5 -0
  17. package/build/database/migrations/1770000000006_kit_messaging.js +10 -0
  18. package/build/database/migrations/1770000000007_kit_webhooks.d.ts +5 -0
  19. package/build/database/migrations/1770000000007_kit_webhooks.js +10 -0
  20. package/build/database/migrations/1770000000008_kit_imports.d.ts +5 -0
  21. package/build/database/migrations/1770000000008_kit_imports.js +10 -0
  22. package/build/database/migrations/1770000000010_kit_workflows.d.ts +5 -0
  23. package/build/database/migrations/1770000000010_kit_workflows.js +10 -0
  24. package/build/index.d.ts +21 -1
  25. package/build/index.js +12 -1
  26. package/build/src/admin/contracts.js +19 -10
  27. package/build/src/admin/controller.d.ts +1 -0
  28. package/build/src/admin/controller.js +6 -0
  29. package/build/src/admin/resource_service.d.ts +21 -0
  30. package/build/src/admin/resource_service.js +163 -7
  31. package/build/src/collaboration/assignments.d.ts +78 -0
  32. package/build/src/collaboration/assignments.js +219 -0
  33. package/build/src/collaboration/record_collaboration.d.ts +86 -0
  34. package/build/src/collaboration/record_collaboration.js +360 -0
  35. package/build/src/commands/agent_assets.js +5 -0
  36. package/build/src/commands/capabilities.d.ts +19 -0
  37. package/build/src/commands/capabilities.js +160 -0
  38. package/build/src/core/message_templates.d.ts +80 -0
  39. package/build/src/core/message_templates.js +288 -0
  40. package/build/src/database/schema.d.ts +18 -0
  41. package/build/src/database/schema.js +192 -0
  42. package/build/src/events/outbox.d.ts +1 -0
  43. package/build/src/events/outbox.js +1 -1
  44. package/build/src/events/record_mutation.d.ts +7 -0
  45. package/build/src/events/record_mutation.js +13 -2
  46. package/build/src/integrations/imports.d.ts +74 -0
  47. package/build/src/integrations/imports.js +331 -0
  48. package/build/src/integrations/openapi.d.ts +39 -0
  49. package/build/src/integrations/openapi.js +256 -0
  50. package/build/src/integrations/print.d.ts +37 -0
  51. package/build/src/integrations/print.js +123 -0
  52. package/build/src/integrations/webhooks.d.ts +98 -0
  53. package/build/src/integrations/webhooks.js +298 -0
  54. package/build/src/resource/define_resource.d.ts +1 -0
  55. package/build/src/resource/define_resource.js +9 -1
  56. package/build/src/resource/registry.d.ts +2 -0
  57. package/build/src/resource/registry.js +9 -0
  58. package/build/src/resource/types.d.ts +3 -0
  59. package/build/src/workflows/define_workflow.d.ts +95 -0
  60. package/build/src/workflows/define_workflow.js +83 -0
  61. package/build/src/workflows/engine.d.ts +119 -0
  62. package/build/src/workflows/engine.js +687 -0
  63. package/package.json +7 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @adula/kit
2
2
 
3
- Experimental 0.2.0-alpha.4 resource framework for AdonisJS 7 and PostgreSQL 17. Node 24 or later is required. MIT licensed. The target is the complete approved 1.0 scope; it has not passed acceptance.
3
+ Resource framework for AdonisJS 7 and PostgreSQL 17, version 1.0.0 (accepted by the owner, ADR 028). Node 24 or later is required. MIT licensed. The public API in `api/kit-api.json` follows semantic versioning.
4
4
 
5
5
  ## Install into a consumer
6
6
 
@@ -28,4 +28,14 @@ Doctor measures the deployment's `storage/uploads` directory and warns above 5 d
28
28
 
29
29
  ## Current limitations
30
30
 
31
- Attachment ownership, upload/download, storage migration, authentication lifecycle/rate limiting, generic resource UI and core administration have local integration consumers and tests. Real OAuth/SMTP, full Tuyau field contracts, human/performance acceptance, XState workflows, later business features, staging/offsite restoration and a genuine minor-version consumer upgrade remain open. Submission envelopes are durable records in pending_definition state, not running workflows. See [implementation status](https://github.com/adulash/adula-kit/blob/main/docs/implementation-status.md), [gaps](https://github.com/adulash/adula-kit/blob/main/KIT_GAPS.md) and [1.0 acceptance](https://github.com/adulash/adula-kit/blob/main/docs/acceptance-1.0.md). Version 0.2.0-alpha.4 targets alpha; the owner-authorized latest alias remains on 0.2.0-alpha.1.
31
+ Phases 3 and 4 (collaboration, assignments, templates and realtime notifications,
32
+ CSV import, printing, webhooks, API tokens/OpenAPI, amend-by-copy and versioned
33
+ XState workflows) are implemented with PostgreSQL tests; see the
34
+ [business features guide](https://github.com/adulash/adula-kit/blob/main/docs/business-features.md).
35
+
36
+ - Two-factor authentication is not included; it is planned for 2.0 (ADR 027).
37
+ - Impersonation was reviewed by automated adversarial and black-box tests only; its
38
+ human security review is deferred to 2.0 (ADR 027).
39
+ - XLSX import is not available (GAP-006); CSV is.
40
+
41
+ See [implementation status](https://github.com/adulash/adula-kit/blob/main/docs/implementation-status.md), [gaps](https://github.com/adulash/adula-kit/blob/main/KIT_GAPS.md) and [1.0 acceptance](https://github.com/adulash/adula-kit/blob/main/docs/acceptance-1.0.md).
@@ -1,13 +1,55 @@
1
- # Current capabilities
1
+ # adula-kit capabilities (1.0.0)
2
2
 
3
- Version 0.2.0-alpha.4, experimental alpha. Resource definitions and registry; six flat condition operators (typed limits documented in KIT_GAPS.md); CASL evaluation with structural ltree scope; JSON/Inertia resource CRUD; field contracts; keyset pagination; sequences/lookups/settings; soft deletion; optimistic concurrency; atomic inline writes/activity/outbox; deduplicated database listeners; org moves; attachment ownership/storage migration; saved views (queries limited to fields the author may query); attachment fields with per-field `accept`/`maxSize` limits and daily pruning of unbound uploads; case-insensitive e-mail identity with ownership proof before OAuth linking; `adula:gaps report`; core administration services; resource/module commands; copied shadcn UI and installation diagnostics.
3
+ Generated by `node ace adula:capabilities`. Read this before proposing a module; anything not listed here is not provided by the kit.
4
4
 
5
- The reference consumer wires authentication/session lifecycle, admin screens, authorized attachments, optional MCP, Redis actor caching, a queue worker, one scheduler and backup/restore drills. Educational modules are isolated test fixtures, not installed application features. The agent ships business frontend-design and idea-review skills, company identity intake and modal defaults. Real SMTP receipt/recovery and isolated offsite local/source-S3 attachment recovery are documented in the source repository's confirmed-achievements ledger. OAuth is optional and externally unverified. Source-S3 recovery is integrated into the operational backup commands and verified directly; local Docker backup/recovery also passed; natural scheduled operation and staging remain pending; each consumer must verify its own configuration.
5
+ ## Resource definition
6
6
 
7
- This is not the 1.0 capability catalog. Planned features are listed in docs/implementation-status.md of the adula-kit source repository; do not assume them.
7
+ | Field type | Storage |
8
+ |---|---|
9
+ | string | varchar |
10
+ | text | text |
11
+ | integer | integer |
12
+ | money | bigint minor units, decimal string in JSON |
13
+ | boolean | boolean |
14
+ | date | date (YYYY-MM-DD) |
15
+ | datetime | timestamptz |
16
+ | json | jsonb |
17
+ | attachment | attachments row id; per-field accept/maxSize |
18
+ | belongsTo | foreign key, preloaded, restrict on delete |
19
+ | hasMany | child resource; inline rows saved with the parent |
20
+ | lookup | lookups group key |
8
21
 
9
- Core user invitations are available through `UserInvitations` and the `core.users/invite` permission. New standalone projects include an Arabic invitation Dialog, SMTP mail and single-use 24-hour acceptance links; recipients set their own passwords and receive no automatic roles. Configure SMTP and APP_URL, then assign business roles after acceptance. Existing consumers must merge project-owned routes/pages deliberately.
22
+ Field options: required, unique (partial, active rows), sortable, searchable (generated tsvector), filterable, permissionLevel, sequence, column.
10
23
 
11
- Administrative mutations protect the last active administrator and the acting administrator's own access. Repeated installation repairs missing bootstrap grants. The reference settings screen controls Gregorian, Umm al-Qura Hijri or dual-calendar presentation, edit-dialog dismissal confirmation and restrained transitions. Storage and API dates stay canonical. Permission actions require confirmation and report server outcomes.
24
+ Resource options: scoped (required), submittable (docStatus, submit/cancel/amend-by-copy), version (optimistic locking, default with submittable), customFields, list / form / show / serialize / hidden, actions, hooks.beforeSave / hooks.afterSave.
12
25
 
13
- The separate @adula/create-app package creates a complete application from an empty directory, including local service provisioning, fresh migrations, an administrator and company identity intake. Node/npm and Docker for the default service mode are prerequisites; existing PostgreSQL/Redis can be used instead. The packages are published on npm under the experimental `alpha` tag: `npm create @adula/app@alpha my-app`.
26
+ Role rule conditions: $eq, $ne, $in, $lt, $gt, $like on scalar fields; unsupported conditions are refused. Organization scope is always added with AND.
27
+
28
+ Workflow steps: condition, update, notify, approval, delay, http, end.
29
+
30
+ ## Services
31
+
32
+ - **Resources and authorization:** `defineResource`, `ResourceRegistry`, `ResourceService`, `createResourceController`, `buildAbility`, `accessibleBy`, `ActorStore`, `packedResourceRules`
33
+ - **Records and collaboration:** `RecordCollaboration`, `followerListeners`, `Assignments`, `SavedViews`, `logActivity`
34
+ - **Documents and workflows:** `defineWorkflow`, `WorkflowEngine`, `workflowListeners`
35
+ - **Notifications and messages:** `notify`, `notifyWithTemplate`, `MessageTemplates`, `deliverNotificationMail`, `listenForNotifications`, `NotificationsAdmin`
36
+ - **Integration:** `Webhooks`, `signWebhook`, `openApiDocument`, `ImportBatches`, `renderPrintHtml`, `htmlToPdf`
37
+ - **Security:** `UserInvitations`, `UsersAdmin`, `RolesAdmin`
38
+ - **Data and operations:** `sequence`, `Settings`, `SettingsAdmin`, `publishOutbox`, `consumeEvent`, `recordMutation`, `moveOrgUnit`, `migrateStorage`, `verifyBackup`, `runtimeHealth`
39
+
40
+ ## Extension points
41
+
42
+ - Resource hooks (beforeSave, afterSave) inside the save transaction
43
+ - Page override: inertia/pages/<resource>/{index,form,show}.tsx replaces the generated page
44
+ - Domain events <module>.<resource>.{created,updated,deleted,submitted,cancelled,amended} with idempotent listeners
45
+ - Module workflows (Module.workflows) with versioned definitions
46
+ - Message templates edited per deployment
47
+ - Outgoing webhooks and the bearer-token /api/v1 API
48
+
49
+ ## Outside the kit
50
+
51
+ - Dynamic fields or a field editor screen
52
+ - Runtime plugins
53
+ - A visual workflow editor
54
+ - Multi-tenant SaaS in one database (one deployment per organization)
55
+ - XLSX import until a maintained parser passes the dependency rule (GAP-006)
@@ -3,4 +3,29 @@ name: idea-review
3
3
  description: Translate a new module or workflow into existing kit resources and declared extension points before writing code.
4
4
  ---
5
5
 
6
- Read docs/decisions, the resource registry, capabilities and KIT_GAPS.md. Identify existing entities before proposing new ones. Produce a compact module blueprint with owned tables, explicit scope, dependencies, relationships, events, field visibility and acceptance tests. Classify each requirement as implemented now, a declared extension, or outside the kit. Do not design a dynamic field editor, runtime plugin loader or visual workflow editor. A user's already-approved plan authorizes its specified reference modules; do not ask to approve them again.
6
+ Use for a new module or workflow only; field changes do not need it.
7
+
8
+ 1. Run `node ace adula:capabilities` and read the output: kit field types, services,
9
+ workflow steps, extension points, what is outside the kit, and this project's
10
+ registered modules, resources and workflows. Read docs/decisions and KIT_GAPS.md.
11
+ 2. Find existing entities with the same meaning; reference them with belongsTo.
12
+ Never duplicate a table owned by another module.
13
+ 3. Produce a compact blueprint:
14
+ - module name, `dependsOn`, owned resources with `scoped` stated explicitly;
15
+ - fields with kit types, required/unique/searchable/filterable, permission levels,
16
+ hidden fields, lookups (groups and keys) and sequences;
17
+ - submittable documents and their workflow as steps (condition, update, notify,
18
+ approval, delay, http, end) with approvers by role;
19
+ - events consumed and emitted, listeners (idempotent) and any webhooks;
20
+ - role rules per action, including conditions from the six operators;
21
+ - acceptance tests: 403/404/uniqueness/scope from the generator plus the
22
+ business rules.
23
+ 4. Classify each requirement: **now** (kit capability), **extension** (hooks, page
24
+ override, listener, workflow), or **outside the kit** (record it in KIT_GAPS.md
25
+ with Needed by, Tried, Blocked because, Proposed kit change, Workaround).
26
+ 5. Never design a dynamic field editor, runtime plugin loader, visual workflow editor
27
+ or a second tenant model. An already-approved plan authorizes its modules; do not
28
+ ask to approve them again.
29
+
30
+ After implementation, run the reviewers: module-review, security-review,
31
+ schema-review, ui-review and perf-review.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: module-review
3
+ description: Review a module's boundaries, dependencies, events and scope before merging.
4
+ ---
5
+
6
+ Check, citing file and line for every finding:
7
+
8
+ - The module lives in `app/modules/<name>` and is registered in `start/modules.ts`
9
+ after every module it depends on; `dependsOn` lists each module whose resources
10
+ it references. `ResourceRegistry.register` must accept the order.
11
+ - It writes only its own tables. Cross-module effects happen in idempotent
12
+ listeners (`consumeEvent` + processed_events), never by importing another
13
+ module's controllers or services. Heavy listeners dispatch jobs.
14
+ - Every resource declares `scoped`. Central (`scoped: false`) resources are
15
+ justified; scoped ones rely on the kit's organization scope, not custom filters.
16
+ - Documents that need approval use `submittable: true` and a module workflow; the
17
+ operational status is a lookup separate from `docStatus`.
18
+ - No kit service is re-implemented under another name (notify, sequence, settings,
19
+ assignments, comments, imports, webhooks, workflows).
20
+ - Educational fixtures are not imported by the application.
21
+
22
+ Verdict: accept, or a numbered list of blocking findings.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: perf-review
3
+ description: Review queries, pagination and background work against the performance budget.
4
+ ---
5
+
6
+ Budget (plan section 13): list with two relations p95 < 300 ms, form save < 200 ms,
7
+ cached Ability build < 5 ms, on 100,000 seeded rows and 50 concurrent users.
8
+
9
+ Check, citing file and line:
10
+
11
+ - Lists go through `ResourceService.list`: keyset pagination, limit ≤ 100, selected
12
+ columns only, belongsTo preloaded in one query per relation; no query in a loop.
13
+ - Custom queries have supporting indexes (foreign keys, `(org_unit_id, deleted_at)`,
14
+ sort columns used by saved views); `EXPLAIN` shows no sequential scan on large
15
+ tables for the common filters.
16
+ - Save paths keep one transaction: record, lines, activity, field changes and outbox.
17
+ Anything slow (mail, HTTP, PDF, imports) runs in the worker, never inline.
18
+ - Listeners are idempotent and cheap or dispatch jobs; workflows do not poll.
19
+ - Caches (Ability, lookups, settings) are invalidated by the kit's revision rules,
20
+ not by time alone.
21
+ - Run `pnpm test:medical` with `K6_BINARY` on staging-like hardware before release
22
+ and record the measured p95 values; never relax the thresholds.
23
+
24
+ Verdict: accept, or findings with the measurement or query plan.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: schema-review
3
+ description: Review migrations and resource definitions for integrity and evolution.
4
+ ---
5
+
6
+ Check, citing file and line:
7
+
8
+ - Migrations are additive (expand/contract): no drop or rename of a column still
9
+ read by the previous release; kit migrations are never edited after release.
10
+ - Standard columns come from the generator (`id`, `org_unit_id` when scoped,
11
+ `created_by`, `updated_by`, timestamps, `deleted_at`, `version`, `doc_status`,
12
+ `amended_from_id`); they are not hand-edited.
13
+ - Every foreign key is indexed and `restrict` on delete (cascade only inside the
14
+ same module); `(org_unit_id, deleted_at)` exists for scoped tables.
15
+ - Unique rules are partial (`WHERE deleted_at IS NULL`); searchable fields produce
16
+ the generated tsvector with a GIN index.
17
+ - Money is bigint minor units; dates use `date`, instants `timestamptz`; changeable
18
+ lists are lookups, numbering uses sequences.
19
+ - The definition's `form`, `list`, `show` and `serialize` match the columns, and
20
+ generated contract tests cover 403/404/uniqueness/scope.
21
+
22
+ Verdict: accept, or blocking findings with the corrected migration.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: security-review
3
+ description: Review authorization, serialization and sensitive flows of a change.
4
+ ---
5
+
6
+ Check, citing file and line:
7
+
8
+ - Every route passes authentication and the resource goes through ResourceService
9
+ (or `resources.access` for record features). Custom queries use `accessibleBy`;
10
+ nothing reads records by id without the scope and Ability checks.
11
+ - Responses use the kit serializers; hidden and `permissionLevel` fields never leave
12
+ the server, including through search, sort, filters, exports, prints, history,
13
+ notifications, webhooks and OpenAPI.
14
+ - Role rules use only the six operators; nothing widens scope with an allow rule.
15
+ - No hand-written crypto, sessions, password or token handling. Secrets are sealed
16
+ with the application encryption; tokens and recovery codes are stored hashed.
17
+ - Outgoing HTTP (webhooks, workflow steps) targets HTTPS and cannot reach private
18
+ addresses in production; payloads carry identifiers, not record fields.
19
+ - Uploads and imports are size-limited and parsed by the kit; errors never echo
20
+ secrets. Rate limits cover authentication, second factor and token creation.
21
+ - 2FA, impersonation and authentication changes are flagged for human review
22
+ (AGENTS rule 11); map the change to ASVS 4.0.3 V2/V3/V4 items.
23
+
24
+ Verdict: accept, or blocking findings with the exploit scenario for each.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: ui-review
3
+ description: Review pages and components for RTL, the component system and data access.
4
+ ---
5
+
6
+ Check, citing file and line:
7
+
8
+ - Components come from `inertia/components/ui` (installed by `node ace adula:ui add`);
9
+ no `shadcn add` in modules, no hand-styled native controls or homemade dialogs.
10
+ - Forms and record details use the shadcn Dialog (ResourceSurface) unless the user
11
+ asked otherwise; destructive actions confirm; edits guard against closing.
12
+ - RTL: `dir="rtl"`, logical properties (`ms-`, `ps-`, `text-start`), arrows follow
13
+ the reading direction, Latin digits in amounts per the UI preference, Arabic
14
+ labels and English identifiers. Dates honor the calendar preference.
15
+ - Generated pages are overridden only through `pages/<resource>/<mode>.tsx`.
16
+ - Heavy sections are deferred props or fetched after render; lists paginate with
17
+ the scroll prop; no page renders data the server did not serialize for the user.
18
+ - Accessible names exist for icon buttons, dialogs and regions; errors use
19
+ `role="alert"`; focus stays inside dialogs.
20
+ - Company identity from docs/design-identity.md is applied, not invented.
21
+
22
+ Verdict: accept, or findings with screenshots or selectors.
@@ -2,5 +2,9 @@ import { BaseCommand } from '@adonisjs/core/ace';
2
2
  export default class Capabilities extends BaseCommand {
3
3
  static commandName: string;
4
4
  static description: string;
5
+ static options: {
6
+ startApp: boolean;
7
+ };
8
+ write: boolean;
5
9
  run(): Promise<void>;
6
10
  }
@@ -1,9 +1,40 @@
1
- import { BaseCommand } from '@adonisjs/core/ace';
2
- import { readFile } from 'node:fs/promises';
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { BaseCommand, flags } from '@adonisjs/core/ace';
8
+ import { writeFile } from 'node:fs/promises';
9
+ import { capabilityCatalog } from '../src/commands/capabilities.js';
3
10
  export default class Capabilities extends BaseCommand {
4
11
  static commandName = 'adula:capabilities';
5
- static description = 'Print implemented capabilities and current acceptance boundary';
12
+ static description = 'Generate the capability catalog (kit features plus this project registry)';
13
+ static options = { startApp: true };
6
14
  async run() {
7
- this.logger.log(await readFile(new URL('../agent/capabilities.md', import.meta.url), 'utf8'));
15
+ let registry;
16
+ try {
17
+ ;
18
+ ({ registry } = await this.app.import('#start/modules'));
19
+ }
20
+ catch {
21
+ this.logger.warning('start/modules could not be loaded; printing kit capabilities only');
22
+ }
23
+ // Imported lazily: main.ts lists this command itself.
24
+ const { getMetaData } = await import('./main.js');
25
+ const metadata = await getMetaData();
26
+ const catalog = capabilityCatalog({
27
+ registry,
28
+ commands: metadata.map((command) => `${command.commandName} — ${command.description}`),
29
+ });
30
+ if (this.write) {
31
+ await writeFile(this.app.makePath('capabilities.md'), catalog);
32
+ this.logger.success('capabilities.md');
33
+ }
34
+ else
35
+ this.logger.log(catalog);
8
36
  }
9
37
  }
38
+ __decorate([
39
+ flags.boolean({ description: 'Write capabilities.md at the project root' })
40
+ ], Capabilities.prototype, "write", void 0);
@@ -1,6 +1,8 @@
1
+ import Resource from './resource.js';
1
2
  import Doctor from './doctor.js';
2
- import Capabilities from './capabilities.js';
3
+ import ModuleAdd from './module_add.js';
4
+ import Ui from './ui.js';
3
5
  export declare function getMetaData(): Promise<import("@adonisjs/core/types/ace").CommandMetaData[]>;
4
6
  export declare function getCommand(meta: {
5
7
  commandName: string;
6
- }): Promise<typeof Capabilities | typeof Doctor | null>;
8
+ }): Promise<typeof Resource | typeof Doctor | typeof ModuleAdd | typeof Ui | null>;
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitCollaboration extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createCollaborationSchema } from '../../src/database/schema.js';
3
+ export default class KitCollaboration extends BaseSchema {
4
+ async up() {
5
+ await createCollaborationSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitAssignments extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createAssignmentsSchema } from '../../src/database/schema.js';
3
+ export default class KitAssignments extends BaseSchema {
4
+ async up() {
5
+ await createAssignmentsSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitMessaging extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createMessagingSchema } from '../../src/database/schema.js';
3
+ export default class KitMessaging extends BaseSchema {
4
+ async up() {
5
+ await createMessagingSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitWebhooks extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createWebhooksSchema } from '../../src/database/schema.js';
3
+ export default class KitWebhooks extends BaseSchema {
4
+ async up() {
5
+ await createWebhooksSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitImports extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createImportsSchema } from '../../src/database/schema.js';
3
+ export default class KitImports extends BaseSchema {
4
+ async up() {
5
+ await createImportsSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ export default class KitWorkflows extends BaseSchema {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseSchema } from '@adonisjs/lucid/schema';
2
+ import { createWorkflowSchema } from '../../src/database/schema.js';
3
+ export default class KitWorkflows extends BaseSchema {
4
+ async up() {
5
+ await createWorkflowSchema(this.db.getWriteClient());
6
+ }
7
+ async down() {
8
+ throw new Error('Kit migrations are additive. Restore a tested backup instead of rolling back.');
9
+ }
10
+ }
package/build/index.d.ts CHANGED
@@ -14,7 +14,25 @@ export { accessibleBy, authorizationSql, conditionSql } from './src/auth/sql.js'
14
14
  export { ResourceService } from './src/admin/resource_service.js';
15
15
  export { serialize, writableInput, selectedFields } from './src/admin/contracts.js';
16
16
  export { KitError } from './src/admin/errors.js';
17
- export { createCoreSchema, createResourceTable, createAttachmentsSchema, createSavedViewsSchema, } from './src/database/schema.js';
17
+ export { createCoreSchema, createResourceTable, createAttachmentsSchema, createSavedViewsSchema, createCollaborationSchema, createAssignmentsSchema, createMessagingSchema, createWebhooksSchema, createImportsSchema, createWorkflowSchema, } from './src/database/schema.js';
18
+ export { defineWorkflow, nextStep } from './src/workflows/define_workflow.js';
19
+ export type { WorkflowDefinition, WorkflowInput, WorkflowStep, WorkflowEvent, StepContext, Recipients, } from './src/workflows/define_workflow.js';
20
+ export { WorkflowEngine, workflowListeners } from './src/workflows/engine.js';
21
+ export type { WorkflowRun, WorkflowRunStatus, WorkflowOptions } from './src/workflows/engine.js';
22
+ export { ImportBatches, importCell, IMPORT_ROW_LIMIT } from './src/integrations/imports.js';
23
+ export type { ImportBatch, ImportStatus, ImportTarget, ActorSource, } from './src/integrations/imports.js';
24
+ export { openApiDocument } from './src/integrations/openapi.js';
25
+ export { renderPrintHtml, htmlToPdf } from './src/integrations/print.js';
26
+ export type { PrintIdentity, PrintInput } from './src/integrations/print.js';
27
+ export { Webhooks, signWebhook, WEBHOOK_MAX_ATTEMPTS } from './src/integrations/webhooks.js';
28
+ export type { SecretBox, Webhook, WebhookDelivery, WebhookOptions, HttpPoster, } from './src/integrations/webhooks.js';
29
+ export { MessageTemplates, DEFAULT_TEMPLATES, renderTemplate, notifyWithTemplate, deliverNotificationMail, listenForNotifications, } from './src/core/message_templates.js';
30
+ export type { TemplateDefinition, MessageTemplate, RenderedMessage, MailSender, NotificationSignal, } from './src/core/message_templates.js';
31
+ export { Assignments } from './src/collaboration/assignments.js';
32
+ export type { Assignment, AssignmentPage, AssignmentStatus, } from './src/collaboration/assignments.js';
33
+ export { RecordCollaboration, followerListeners } from './src/collaboration/record_collaboration.js';
34
+ export type { ActorLoader, CommentEntry, FieldChangeEntry, RecordCollaborationState, MentionCandidate, } from './src/collaboration/record_collaboration.js';
35
+ export type { FieldChange } from './src/events/record_mutation.js';
18
36
  export { registerUpload, findAttachment, loadAttachments, claimAttachment, releaseAttachment, summarizeAttachment, attachmentUrl, isAttachmentId, isRelativeDiskPath, attachmentPolicy, pendingUploadCount, staleUploads, forgetUpload, DEFAULT_ATTACHMENT_EXTENSIONS, PENDING_UPLOAD_LIMIT, UNBOUND_UPLOAD_TTL_MS, } from './src/attachments/attachment_service.js';
19
37
  export type { AttachmentRecord, AttachmentSummary, UploadInput, ClaimInput, } from './src/attachments/attachment_service.js';
20
38
  export { migrateStorage } from './src/attachments/storage_migrate.js';
@@ -49,3 +67,5 @@ export { runtimeHealth, isBackupStale, backupStale, heartbeat, HEARTBEAT_MAX_AGE
49
67
  export type { RuntimeHealth, HeartbeatStatus, QueueSnapshot, QueueCounts, FailedJob, } from './src/core/health.js';
50
68
  export { MailDeliveryTest, MAIL_TEST_KEY, type MailTestState } from './src/core/mail_test.js';
51
69
  export { InitialSetup, type SetupCheck } from './src/core/setup.js';
70
+ export { KIT_VERSION } from './src/version.js';
71
+ export { capabilityCatalog, SERVICES as CAPABILITY_SERVICES, FIELD_TYPES, WORKFLOW_STEPS, } from './src/commands/capabilities.js';
package/build/index.js CHANGED
@@ -10,7 +10,16 @@ export { accessibleBy, authorizationSql, conditionSql } from './src/auth/sql.js'
10
10
  export { ResourceService } from './src/admin/resource_service.js';
11
11
  export { serialize, writableInput, selectedFields } from './src/admin/contracts.js';
12
12
  export { KitError } from './src/admin/errors.js';
13
- export { createCoreSchema, createResourceTable, createAttachmentsSchema, createSavedViewsSchema, } from './src/database/schema.js';
13
+ export { createCoreSchema, createResourceTable, createAttachmentsSchema, createSavedViewsSchema, createCollaborationSchema, createAssignmentsSchema, createMessagingSchema, createWebhooksSchema, createImportsSchema, createWorkflowSchema, } from './src/database/schema.js';
14
+ export { defineWorkflow, nextStep } from './src/workflows/define_workflow.js';
15
+ export { WorkflowEngine, workflowListeners } from './src/workflows/engine.js';
16
+ export { ImportBatches, importCell, IMPORT_ROW_LIMIT } from './src/integrations/imports.js';
17
+ export { openApiDocument } from './src/integrations/openapi.js';
18
+ export { renderPrintHtml, htmlToPdf } from './src/integrations/print.js';
19
+ export { Webhooks, signWebhook, WEBHOOK_MAX_ATTEMPTS } from './src/integrations/webhooks.js';
20
+ export { MessageTemplates, DEFAULT_TEMPLATES, renderTemplate, notifyWithTemplate, deliverNotificationMail, listenForNotifications, } from './src/core/message_templates.js';
21
+ export { Assignments } from './src/collaboration/assignments.js';
22
+ export { RecordCollaboration, followerListeners } from './src/collaboration/record_collaboration.js';
14
23
  export { registerUpload, findAttachment, loadAttachments, claimAttachment, releaseAttachment, summarizeAttachment, attachmentUrl, isAttachmentId, isRelativeDiskPath, attachmentPolicy, pendingUploadCount, staleUploads, forgetUpload, DEFAULT_ATTACHMENT_EXTENSIONS, PENDING_UPLOAD_LIMIT, UNBOUND_UPLOAD_TTL_MS, } from './src/attachments/attachment_service.js';
15
24
  export { migrateStorage } from './src/attachments/storage_migrate.js';
16
25
  export { diagnoseAttachments } from './src/attachments/doctor.js';
@@ -32,3 +41,5 @@ export { SettingsAdmin, isProtectedSetting, parseSettingValue, SETTING_SCOPES, P
32
41
  export { runtimeHealth, isBackupStale, backupStale, heartbeat, HEARTBEAT_MAX_AGE_MS, BACKUP_MAX_AGE_MS, } from './src/core/health.js';
33
42
  export { MailDeliveryTest, MAIL_TEST_KEY } from './src/core/mail_test.js';
34
43
  export { InitialSetup } from './src/core/setup.js';
44
+ export { KIT_VERSION } from './src/version.js';
45
+ export { capabilityCatalog, SERVICES as CAPABILITY_SERVICES, FIELD_TYPES, WORKFLOW_STEPS, } from './src/commands/capabilities.js';
@@ -19,18 +19,27 @@ export function selectedFields(resource, ability, options = {}) {
19
19
  ]),
20
20
  ].filter((key) => key !== 'orgPath' && resource.fields[key]?.type !== 'hasMany');
21
21
  }
22
+ const rowKeys = new WeakMap();
23
+ /** Record keys and their columns for a resource, computed once per definition. */
24
+ function keysOf(resource) {
25
+ let keys = rowKeys.get(resource);
26
+ if (!keys) {
27
+ keys = [
28
+ ...standard,
29
+ 'orgUnitId',
30
+ 'version',
31
+ 'docStatus',
32
+ 'amendedFromId',
33
+ 'orgPath',
34
+ ...Object.keys(resource.fields),
35
+ ].map((key) => [key, resource.fields[key]?.column ?? columnName(key)]);
36
+ rowKeys.set(resource, keys);
37
+ }
38
+ return keys;
39
+ }
22
40
  export function fromRow(row, resource) {
23
41
  const result = {};
24
- for (const key of [
25
- ...standard,
26
- 'orgUnitId',
27
- 'version',
28
- 'docStatus',
29
- 'amendedFromId',
30
- 'orgPath',
31
- ...Object.keys(resource.fields),
32
- ]) {
33
- const column = resource.fields[key]?.column ?? columnName(key);
42
+ for (const [key, column] of keysOf(resource)) {
34
43
  if (column in row) {
35
44
  const value = row[column];
36
45
  result[key] =
@@ -22,6 +22,7 @@ export interface ResourceController {
22
22
  destroy(ctx: HttpContext): Promise<unknown>;
23
23
  submit(ctx: HttpContext): Promise<unknown>;
24
24
  cancel(ctx: HttpContext): Promise<unknown>;
25
+ amend(ctx: HttpContext): Promise<unknown>;
25
26
  create(ctx: HttpContext): Promise<unknown>;
26
27
  edit(ctx: HttpContext): Promise<unknown>;
27
28
  options(ctx: HttpContext): Promise<unknown>;
@@ -27,6 +27,7 @@ export function createResourceController(resolveRuntime, renderList, renderForm,
27
27
  sort: ctx.request.input('sort'),
28
28
  direction: ctx.request.input('direction'),
29
29
  filters: ctx.request.input('filters'),
30
+ tag: ctx.request.input('tag'),
30
31
  estimate: ctx.request.input('estimate') !== 'false' && ctx.request.input('estimate') !== false,
31
32
  });
32
33
  return renderList && ctx.request.accepts(['html', 'json']) === 'html'
@@ -70,6 +71,11 @@ export function createResourceController(resolveRuntime, renderList, renderForm,
70
71
  async cancel(ctx) {
71
72
  return this.#transition(ctx, 'cancel');
72
73
  }
74
+ async amend(ctx) {
75
+ return this.#execute(ctx, async ({ resources, actor }, resource) => ctx.response.created({
76
+ data: await resources.amend(resource.name, this.#id(ctx), actor),
77
+ }));
78
+ }
73
79
  #id(ctx) {
74
80
  return this.#positiveId(ctx.params.id);
75
81
  }
@@ -10,6 +10,8 @@ export type ListOptions = {
10
10
  sort?: string;
11
11
  direction?: 'asc' | 'desc';
12
12
  filters?: Record<string, unknown>;
13
+ /** Only records carrying this tag (see RecordCollaboration.setTags). */
14
+ tag?: string;
13
15
  estimate?: boolean;
14
16
  };
15
17
  /**
@@ -21,6 +23,8 @@ export declare class ResourceService {
21
23
  private db;
22
24
  private registry;
23
25
  constructor(db: Knex, registry: ResourceRegistry);
26
+ /** The Arabic label of a registered resource, for notifications and titles. */
27
+ label(name: string): string;
24
28
  /** Project navigation is derived from registered resources and the current actor. */
25
29
  navigation(actor: Actor): ResourceNavigation;
26
30
  describe(name: string, actor: Actor): ResourceDescription;
@@ -34,6 +38,17 @@ export declare class ResourceService {
34
38
  private columns;
35
39
  private find;
36
40
  private requireRecord;
41
+ /**
42
+ * Authorizes one record for collaboration features (comments, tags, assignments...).
43
+ * Scope and conditional rules apply exactly as for show/update.
44
+ */
45
+ access(name: string, id: number, actor: Actor, action?: Action, db?: Knex): Promise<{
46
+ resource: Resource;
47
+ record: RecordData;
48
+ ability: KitAbility;
49
+ }>;
50
+ /** Whether a (possibly other) actor may perform an action on a record; never throws for denial. */
51
+ permits(name: string, id: number, actor: Actor, action?: Action): Promise<boolean>;
37
52
  list(name: string, actor: Actor, options?: ListOptions): Promise<{
38
53
  data: SerializedRecord[];
39
54
  permissions: {
@@ -190,5 +205,11 @@ export declare class ResourceService {
190
205
  /** Inline children share the parent's draft state, organization and update authority. */
191
206
  private requireInlineParents;
192
207
  transition(name: string, id: number, actor: Actor, action: 'delete' | 'submit' | 'cancel', version?: unknown, transaction?: Knex.Transaction): Promise<SerializedRecord>;
208
+ /**
209
+ * Amend-by-copy: a cancelled document is copied into a new draft that points to
210
+ * it through amended_from_id, together with its inline lines. The original stays
211
+ * cancelled and unchanged; sequence fields receive new numbers.
212
+ */
213
+ amend(name: string, id: number, actor: Actor, transaction?: Knex.Transaction): Promise<SerializedRecord>;
193
214
  private audit;
194
215
  }