From 7ba038e972cc99d4c5dd7a6fcd783291b1f22ebf Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 02:11:07 +0000 Subject: [PATCH] chore(deps): upgrade @objectstack/* from 17.7.0 to 18.1.0 Walks the official 18.0.0 and 18.1.0 upgrade checklists: - pin every @objectstack/* dependency to exactly 18.1.0 and hold the CLI's own ranges with a pnpm override (18.1 checklist step 1) - declare protocol ^18 in the stack manifest and objectstack.manifest.json - rewrite every flow value slot from the retired {path} template dialect to a CEL value envelope (ref/must in src/flows/_values.ts) and every text slot to {{ }} holes (ADR-0032, 252 refusals) - drop page template 'full-width', removed in spec 18 - bind positions to permission sets through permissionSets on the position definition; the sys_position_permission_set binder retires with the object - add the zh-CN (and mirrored en) keys 18.1's flow i18n surface now walks: screen descriptions, select options, successMessage, approval step labels Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01LWgCfQwCTdEDv21fUM79aB --- README.md | 2 +- objectstack.config.ts | 20 +- objectstack.manifest.json | 4 +- package.json | 8 +- pnpm-lock.yaml | 1254 +++++++++++++-------------- pnpm-workspace.yaml | 13 + src/apps/clm.app.ts | 5 +- src/flows/_daily-sweep.ts | 25 +- src/flows/_values.ts | 54 ++ src/flows/contract-approval.flow.ts | 31 +- src/flows/contract-intake.flow.ts | 97 ++- src/flows/executed-upload.flow.ts | 47 +- src/flows/expiration-sweep.flow.ts | 25 +- src/flows/legal-review-sla.flow.ts | 17 +- src/flows/obligation-due.flow.ts | 27 +- src/flows/payment-overdue.flow.ts | 15 +- src/flows/renewal-notice.flow.ts | 108 +-- src/flows/signature-record.flow.ts | 41 +- src/flows/turn-stalled.flow.ts | 9 +- src/pages/contract_detail.page.ts | 1 - src/profiles/requester.profile.ts | 2 +- src/security/bind-position-sets.ts | 142 --- src/security/index.ts | 2 - src/sharing/positions.ts | 30 +- src/translations/en/flows.ts | 35 +- src/translations/zh-CN/flows.ts | 34 +- 26 files changed, 1010 insertions(+), 1038 deletions(-) create mode 100644 src/flows/_values.ts delete mode 100644 src/security/bind-position-sets.ts delete mode 100644 src/security/index.ts diff --git a/README.md b/README.md index de59a37..894b003 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ POST /api/v1/data/sys_user_position ``` A name-spelled row grants that position's own permission set **and** `clm_requester`, because every -position binds that set too (`src/security/bind-position-sets.ts`) — a lawyer holds `clm_legal` and +position names that set too (`permissionSets` in `src/sharing/positions.ts`) — a lawyer holds `clm_legal` and `clm_requester` both. To read back what an account actually ended up with: ```http diff --git a/objectstack.config.ts b/objectstack.config.ts index 5c3649b..17e5b81 100644 --- a/objectstack.config.ts +++ b/objectstack.config.ts @@ -5,7 +5,6 @@ import * as actions from './src/actions/index.js'; import { allFlows, flowFunctions } from './src/flows/index.js'; import * as profiles from './src/profiles/index.js'; import { ClmPositions, ClmSharingRules } from './src/sharing/index.js'; -import { registerClmPositionBindings, type BindHostContext } from './src/security/index.js'; import { clmSeeds } from './src/data/index.js'; import * as apps from './src/apps/index.js'; import * as datasets from './src/datasets/index.js'; @@ -32,7 +31,7 @@ export default defineStack({ // Protocol major this app is authored against. The runtime checks the // range at load time and refuses a major-incompatible runtime with a // structured diagnostic instead of failing deep in a schema parse. - engines: { protocol: '^17' }, + engines: { protocol: '^18' }, }, // `ui` serves the Console; `auth` mounts the login surface and the platform @@ -193,8 +192,8 @@ export default defineStack({ functions: flowFunctions, // Security (DESIGN.md §04, card 04): seven positions, five permission sets, - // the sharing rules. Positions and sets are containers — the join rows that - // make a position grant anything are written by `onEnable` below. + // the sharing rules. Each position names the sets it distributes in its own + // `permissionSets` (src/sharing/positions.ts). positions: ClmPositions, permissions: Object.values(profiles), sharingRules: ClmSharingRules, @@ -206,16 +205,3 @@ export default defineStack({ // `OS_SEED_LOCALE` chooses which language the fixture is written in. data: clmSeeds, }); - -/** - * Declaring a position and a permission set grants nobody anything until a - * `sys_position_permission_set` row joins them, and that row cannot be a seed - * (the seed loader runs before the security bootstrap creates the rows it - * would reference). Bind them on `kernel:bootstrapped` instead — the shape - * ATS ships. A module-level named export, which the CLI preserves beside the - * default `defineStack()` export on the config-load path and grafts back onto - * the app bundle on the artifact-boot path. - */ -export const onEnable = async (ctx: unknown): Promise => { - registerClmPositionBindings(ctx as BindHostContext); -}; diff --git a/objectstack.manifest.json b/objectstack.manifest.json index b687689..d15edf6 100644 --- a/objectstack.manifest.json +++ b/objectstack.manifest.json @@ -2,9 +2,9 @@ "$schema": "https://schemas.objectstack.dev/template-manifest.json", "name": "hotclm", "namespace": "clm", - "specVersion": "^17.0.0", + "specVersion": "^18.0.0", "engines": { - "protocol": "^17" + "protocol": "^18" }, "manifestId": "app.objectstack.hotclm", "displayName": "HotCLM", diff --git a/package.json b/package.json index d3e1919..a3866b8 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,12 @@ "verify": "pnpm validate && pnpm lint && pnpm typecheck && pnpm lint:i18n-gate" }, "dependencies": { - "@objectstack/spec": "^17.7.0", - "@objectstack/runtime": "^17.7.0", - "@objectstack/plugin-hono-server": "^17.7.0" + "@objectstack/spec": "18.1.0", + "@objectstack/runtime": "18.1.0", + "@objectstack/plugin-hono-server": "18.1.0" }, "devDependencies": { - "@objectstack/cli": "^17.7.0", + "@objectstack/cli": "18.1.0", "typescript": "^6.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54f206a..356a1ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,23 +4,33 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@objectstack/core': 18.1.0 + '@objectstack/driver-memory': 18.1.0 + '@objectstack/driver-mongodb': 18.1.0 + '@objectstack/driver-sql': 18.1.0 + '@objectstack/driver-sqlite-wasm': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/runtime': 18.1.0 + importers: .: dependencies: '@objectstack/plugin-hono-server': - specifier: ^17.7.0 - version: 17.7.0 + specifier: 18.1.0 + version: 18.1.0 '@objectstack/runtime': - specifier: ^17.7.0 - version: 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + specifier: 18.1.0 + version: 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) '@objectstack/spec': - specifier: ^17.7.0 - version: 17.7.0 + specifier: 18.1.0 + version: 18.1.0 devDependencies: '@objectstack/cli': - specifier: ^17.7.0 - version: 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + specifier: 18.1.0 + version: 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) typescript: specifier: ^6.0.0 version: 6.0.3 @@ -31,73 +41,70 @@ packages: resolution: {integrity: sha512-cTlrKttbrRHEw3W+0/I609A2Matj5JQaRvfLtEIGZvlN0RaPi+3ANsMeqAyCAVlH/lUIW2tmtBlSMni74lcXeg==} engines: {node: '>=12'} - '@better-auth/core@1.7.3': - resolution: {integrity: sha512-JdP7lOkyE83jgjn7RilJj1XvZ7n2JjRsErKJuaXchjyuNo6cf1iVd3GtbhAtiUyJJkWdk8yL+LaUBKT80H0zLA==} + '@better-auth/core@1.7.12': + resolution: {integrity: sha512-SUStBGHJ13KnSO6+7U/1DSlfuOtln4jtOrFZqzQtxZVHZIrAoMjwHNGitlF+s7hvKbuC3Z14xjnqfLLqIqGfxw==} peerDependencies: - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@cloudflare/workers-types': '>=4' + '@better-auth/utils': ^0.4.2 || ^0.5.0 + '@better-fetch/fetch': 1.3.2 '@opentelemetry/api': ^1.9.0 - better-call: 1.4.0 + better-call: 1.4.4 jose: ^6.1.0 kysely: ^0.28.5 || ^0.29.0 nanostores: ^1.0.1 peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true '@opentelemetry/api': optional: true - '@better-auth/drizzle-adapter@1.7.3': - resolution: {integrity: sha512-S+nQRlxbUhkR43LrSv8c98ZvOvmv3nrtOnHkiZXkdDkr60PWp7maC2cqgzZ2C9exCC1a+4TugJlx2jRL+r+/9A==} + '@better-auth/drizzle-adapter@1.7.12': + resolution: {integrity: sha512-Qzoyuf05aK01Pc0k3EXv8Xj3XCT2m2dCWc5HoB7x+xX6N3Pm2eQsca+UIArsJcbuNEWWP3L51oIebnBoHJ5DTw==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 peerDependenciesMeta: drizzle-orm: optional: true - '@better-auth/kysely-adapter@1.7.3': - resolution: {integrity: sha512-UIsyJMIrjUnT+yTaS6dkCxYYmtPwxFHxwSJ8+CLty2II5w9BewlDxDA0/QzhoL/InYCPxQ5Y6xIgLHZG1dhwRA==} + '@better-auth/kysely-adapter@1.7.12': + resolution: {integrity: sha512-1PaFXktLv4n3DmrGbPaCj5SsvcT40NnWw7NLua5/J1+8/A051N5ZzqOwJifXEIQX5k6NdwvmymwDQxKytduEAQ==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 kysely: ^0.28.17 || ^0.29.0 peerDependenciesMeta: kysely: optional: true - '@better-auth/memory-adapter@1.7.3': - resolution: {integrity: sha512-WdLANFY/QWC3G351RCzxU+Y9YlW+BQ1oG9NwBTSOUWQw5rZ87ws+weU8tvAMO7sQ4C9gKIlkOKBKUKXrSt91Tw==} + '@better-auth/memory-adapter@1.7.12': + resolution: {integrity: sha512-glaDwXzDptnddHqRuu7fj47FAssUcOzLNgdlTGJoEPrspkX69x2vnXwgAzNuApjZI21WLtPH/rQ6CN+DQGybPA==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 - '@better-auth/mongo-adapter@1.7.3': - resolution: {integrity: sha512-YL9m01tNogmFmRvOWJ46M9WwE6HirCXHDell29mtsQB/Qs1TPNLrgj7ybGMMGhQuyj6S218+8Wbls8m9s+i8RQ==} + '@better-auth/mongo-adapter@1.7.12': + resolution: {integrity: sha512-HKV4sgYSw/Gdt9AuHlESMkiKL72SWdGweDy1Wsyxoh06BpG7aNu4aSJ7marNpMdKZteyRbxWWqQkA9ediA8qFw==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: optional: true - '@better-auth/oauth-provider@1.7.3': - resolution: {integrity: sha512-Eoajj68F1ETmp4NBM98tgj1Uiip4cZiFqFsijAWIvOEQTejr/67PWK1/92N8PiNdLf03ezUWU538W93rd9fyIw==} + '@better-auth/oauth-provider@1.7.12': + resolution: {integrity: sha512-XzjlApbWtuuoEK63CUNHbgs8zpr3AjlQeGbT1vWEPqvOvI2z4lOaIvIsogomfqFAgj/jgB0zMGYjt0MRyW0T7w==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - better-auth: ^1.7.3 - better-call: 1.4.0 - - '@better-auth/prisma-adapter@1.7.3': - resolution: {integrity: sha512-TJ/DhlU7oLzrC626/1wfYA1Pl+lVsXa/zXBZ8d7Rlc2YO5fGd5fsAYJdRrYMyA51iZEo+rWLXMhZ0cez1BamsQ==} + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 + '@better-fetch/fetch': 1.3.2 + better-auth: ^1.7.12 + better-call: 1.4.4 + + '@better-auth/prisma-adapter@1.7.12': + resolution: {integrity: sha512-FZRX6ORbwhDmeYE1Tl0IPtUn8v3BlVFgbJxDnm/DCNHxuFRf+0t4HfddUgYeYehHFR8aSDsdYX44fEFWM9877w==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 peerDependenciesMeta: @@ -106,38 +113,35 @@ packages: prisma: optional: true - '@better-auth/scim@1.7.3': - resolution: {integrity: sha512-1/L7zIEUAb1jxlzWKkqbDAD4Jz7u83yZg15TSaIaKcFe1dpCdNxkzQRrjbPLUfnVtBh/vnk10uLxVFCtbMdrdg==} + '@better-auth/scim@1.7.12': + resolution: {integrity: sha512-mmDGQr9tu6z0mUhuQ5zGlutU0wmg3zujuP2uz02FqVil7RdEsHuYI6THeahF2EQEmCQ4E241SI8Lp94OruQw3A==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 - better-auth: ^1.7.3 - better-call: 1.4.0 + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 + better-auth: ^1.7.12 + better-call: 1.4.4 - '@better-auth/sso@1.7.3': - resolution: {integrity: sha512-inxETRuOfyy8m0uEJSfi9uhScp4RkQ7ZYI+GRj+Jtz6ml/p5wTzNOjYZsG43TroUTDfrElF6CYQON5/Xk1XdmA==} + '@better-auth/sso@1.7.12': + resolution: {integrity: sha512-fxUD9+MjNRhuT+nVtFgqPvWAvwWJVacuIijdf4x2nCHqR44xfFgYYWK2iB6vOiin9Qxof9kTPKvRl7ZCJHdwFQ==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - better-auth: ^1.7.3 - better-call: 1.4.0 - - '@better-auth/telemetry@1.7.3': - resolution: {integrity: sha512-aixgHbJhGvS8PRczX/LR3murYyBnIvOGmJw37ZZBMg6ZtLR/UBJAkufbDi1HYn5THSuTJ3D5DtY85Ahh/ABQtw==} + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 + '@better-fetch/fetch': 1.3.2 + better-auth: ^1.7.12 + better-call: 1.4.4 + + '@better-auth/telemetry@1.7.12': + resolution: {integrity: sha512-DaDkKNnI99vKGGbWq9KJg2uYeaOjsK9nIBw9T2m2KG/RkxpvQRjYkZ00++up2bZQkR5pB1nukGbplza3UJG/Xg==} peerDependencies: - '@better-auth/core': ^1.7.3 - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - - '@better-auth/utils@0.4.2': - resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} + '@better-auth/core': ^1.7.12 + '@better-auth/utils': ^0.4.2 || ^0.5.0 + '@better-fetch/fetch': 1.3.2 '@better-auth/utils@0.5.0': resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} - '@better-fetch/fetch@1.3.1': - resolution: {integrity: sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==} + '@better-fetch/fetch@1.3.2': + resolution: {integrity: sha512-Gs7n99b5tqUC6cQAPbV0uED3IraHB6xQbHLQ/C3l7ZFafHScOx9pQ+DYmP5blbLFShVWLqxNUlI9wi4xU/X+ow==} '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} @@ -364,45 +368,48 @@ packages: '@nodable/entities@3.0.0': resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} - '@objectstack/account@17.7.0': - resolution: {integrity: sha512-nPeR21ndVZajDaiy/g0RBupG3NGuDXlzN7daag+cxl+iDKS4TzWvfqnp8Pnr8UnRJ+Et93CSaVLjUZRxHhBVow==} + '@objectstack/account@18.1.0': + resolution: {integrity: sha512-WNMglqHxcdu9Ht2kF9IEOcQ8LaYBE9p5uwyl9a+9fnZ7GxQqv0h7sYTsBGBek/1rEYVFJiC9JBOeGJyJQJK3Ow==} engines: {node: '>=22.0.0'} - '@objectstack/cli@17.7.0': - resolution: {integrity: sha512-WTJ5B/uYSmqlMmqiGy7Acu8M45kHfouothcedRV1XldbKE+1lDGp+Gxg7bNXgqoLU/dmuvYz3JvMs2TPedKNkQ==} + '@objectstack/cli@18.1.0': + resolution: {integrity: sha512-CaWt2tDYJTKR5RvMvWfpeVe5aFEQHXkDts/wV6bZmsR6veh2byKiKjubGRJkepvbj/FnjlgsHEuMf6zIW+sLdQ==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@objectstack/driver-turso': ^17.7.0 + '@objectstack/driver-turso': ^18.1.0 peerDependenciesMeta: '@objectstack/driver-turso': optional: true - '@objectstack/client@17.7.0': - resolution: {integrity: sha512-PFnr58tb1usCSMP+jNyuMBn9a6fEcaVFOiGRox9B88VQvttXo99akGzI8utFX8DsEobLQbh9YZBtXR51VI+JQg==} + '@objectstack/client@18.1.0': + resolution: {integrity: sha512-6Ih4vPl03Edr6CZkBKCdqS+6rgGwBuj3MZFr+yAh8RlS42YirjsR737+EucgQrHqHAPKYCUSZLNvA8xm+ADyag==} engines: {node: '>=22.0.0'} - '@objectstack/cloud-connection@17.7.0': - resolution: {integrity: sha512-NGBWESNZVX63NIwiwZOF8tNlsqw6KQARtET6oHd+BaTLzRs9Bk1bCkRifoDgrlom9029rRgWM/v80JiOPWgkGQ==} + '@objectstack/cloud-connection@18.1.0': + resolution: {integrity: sha512-OmoTLKjN1J2G3TrD2+U4kijjnHr7l+9oEHTH0RL9ncLuFZAE+e9OvJQY+2AfLBOah1Ykf56GcUkFVgiJxsjCnQ==} engines: {node: '>=22.0.0'} - '@objectstack/console@17.7.0': - resolution: {integrity: sha512-4Z8Fy47VHzHtLk82sxf5I6J3SLUhT5NetZLI1YccBvkyMe4stzwx7GKCFAzK+MSKGE3Arv+PGIdqCuvNCd4neA==} + '@objectstack/connector-imap@18.1.0': + resolution: {integrity: sha512-PYgITDeSecgnHCMLSQ2zLy6rSsO2wjqz11sy33601m+uxhLiuYrEi18XikT9XcYzz7EB1xizz8SwVDcFjjRhKQ==} - '@objectstack/core@17.7.0': - resolution: {integrity: sha512-2/NqNk41eNo0nPgBsClefme2HwtLhwCdxKZER+XTOD345KAb4L0suyu2vGxHAN82MGkfNu4ticZzrRAHDT7tYQ==} + '@objectstack/console@18.1.0': + resolution: {integrity: sha512-LubT9vt6tAQmYykaqbh2dTyuEgWlXowpY+Jecm3yO1bgtzy2hlIByNMQmvJ/O2XlvVUzq2ArqPObFsHeaQdSeQ==} + + '@objectstack/core@18.1.0': + resolution: {integrity: sha512-VeKMMDB3ZMowtDRrfzUjLKdo/biErfLn13Ewh/I+HJVETs7eVrcOyyN9TjXkohf8SQdDhYny1QrxaiUBUxi16w==} engines: {node: '>=22.0.0'} - '@objectstack/driver-memory@17.7.0': - resolution: {integrity: sha512-AkSMFi+CJeNxEvLhSVc2I9H9KUHEZgNC6ESxJE36SWxYjD6EsbFoHEjWUvUoiOVr0ETOJIvgjySIms3Ix2vaLQ==} + '@objectstack/driver-memory@18.1.0': + resolution: {integrity: sha512-TdLzG7+iBmmfIY2Z/jd9LbKOeqzxAhFDBlOaPUcS3PGkg/y7GwO17HuQDqPC5RcIo4QVSxv/9TCXhMBWWygBCQ==} engines: {node: '>=22.0.0'} - '@objectstack/driver-mongodb@17.7.0': - resolution: {integrity: sha512-6iBxiXol0Bb93P1X7DM60e1Br7omycl65aSVUWr+3g9rr9tw+MYOK0R+SwmKfszW6TrWJxw6NN0OmNbl8/g+3Q==} + '@objectstack/driver-mongodb@18.1.0': + resolution: {integrity: sha512-13n6BWwwSlC/OCfBl+A4EM1Z0F9P9VniH2tvI0cOwz+MNPfObT6Wa9A63VXAg5/MY0OPYSKuimJz61pcnzjsDA==} engines: {node: '>=22.0.0'} - '@objectstack/driver-sql@17.7.0': - resolution: {integrity: sha512-4gYyyvW5UFPzc0im+Q49EVLAK1HS8voAex69Pvlm/TIFXFKzSVmKwGy9xVYgFxISkHBjoCcdji+WRYSab0hIig==} + '@objectstack/driver-sql@18.1.0': + resolution: {integrity: sha512-4X1sYv5Bn6u1BUZKlQ2BRO0VKKUjS8uEX0lGKzOqcqDE5yJFl7e1wtU/4gt6WcKhbsQCmDC4/h0K9uarU8Xbmw==} engines: {node: '>=22.0.0'} peerDependencies: mysql2: ^3.0.0 @@ -416,122 +423,117 @@ packages: tedious: optional: true - '@objectstack/driver-sqlite-wasm@17.7.0': - resolution: {integrity: sha512-47ZZRIjCvLYOVOlSPsqyN+fLmKqnR5EoB0Xq5us+xZb2x+Qp32AjDRB+goz+HbJy69YmLq217hWCqmbLYfFH7w==} + '@objectstack/driver-sqlite-wasm@18.1.0': + resolution: {integrity: sha512-BKACgB6wsFjysKy4gZ+Oe8RmZcLNK8VKhm+QLjsfLQbF0C4Bgqil7ZEEbS/5OxJpVd+pEPP8v2JAK/KcBnm+TQ==} engines: {node: '>=22.0.0'} - '@objectstack/formula@17.7.0': - resolution: {integrity: sha512-jf7SA/8QawVEKmQhwxjtyGxz+DIX+3V7M/Kt3z79jMhby5tjh5vwTbJI3PdyWCKQ1pS0epO6sSK7MddwLbEJ9Q==} + '@objectstack/formula@18.1.0': + resolution: {integrity: sha512-3zyGz5t87F5pOcgt/1oyezcDvwOTUbQrbROZiI7t3AMmTl/mm774QcDhT4h/kFRBTUeQx0T7+M8pqTKJ1iFrSA==} - '@objectstack/lint@17.7.0': - resolution: {integrity: sha512-ddCHHRJKRSEirb74UNWd8DwdpqvQtMzsKH/bBiUslwXlYucQJPlQMd+ZVYN4V0IJoeD+IpD5el3P6S5H/wqPuQ==} + '@objectstack/lint@18.1.0': + resolution: {integrity: sha512-m9wSzH1+YhVZgB3gf07BIStWzZyLQkMgreAeIEKjc4jM2Wi8HPWzanibfNb2MBlph/M/E5w55yJNGlm+w6mOnw==} engines: {node: '>=22.0.0'} - '@objectstack/mcp@17.7.0': - resolution: {integrity: sha512-WfAFbOjECCbXPUcEciSHd35P87Eagxzkz9gQm6YwbdIz7dvOJxikaqvQpG/Vq64rDG6K7u9nvL/BSYklbH9BEQ==} + '@objectstack/mcp@18.1.0': + resolution: {integrity: sha512-Uu9LfFAKpYwEnXpNQLyZFeuB1wR6sPpSSwKru6wEpgMQQ9gT5QiPpeEzBak2vsWKc+Awf9w5hPnUC09gbQ9QbA==} engines: {node: '>=22.0.0'} - '@objectstack/metadata-core@17.7.0': - resolution: {integrity: sha512-dMDA0wZ0x6HBy+A9C4CjriK/NnUPKybSY/e/kPa7XUxe7mpoqfJlgwCC7RJs2HuHlKVJHL4VOn4NBcYaYzkwQg==} + '@objectstack/metadata-core@18.1.0': + resolution: {integrity: sha512-5rllDa/+dEgGB6/KFOplSF9OsvEW3gflWVxqJvVCKYoMhpkLDJspy8h6Sx8zDy54yPCi1/YliwNBZgY0EAnhQQ==} engines: {node: '>=22.0.0'} - peerDependencies: - vitest: ^4.0.0 - peerDependenciesMeta: - vitest: - optional: true - '@objectstack/metadata-fs@17.7.0': - resolution: {integrity: sha512-jTn/xEGy8R9RbTnNalKxymHubjvloglYneRcFa/1I1/JuUllWgJsgogMHK2hG/pHwVOj1dN/WxgvmxTyMi36Lw==} + '@objectstack/metadata-fs@18.1.0': + resolution: {integrity: sha512-2WvSOQdOUlB9C0VV+1ublW7iSUmzZDShb5zAAcUST9JKOjj9R9TkXVYINQIzBNzGcsw9qrgesUFhlb8IULyOEA==} engines: {node: '>=22.0.0'} - '@objectstack/metadata-protocol@17.7.0': - resolution: {integrity: sha512-fQIviSz1DxMeq8+2Tg83pU6EcSVkoQKagsB9XdepJPmDtPzdiPSgZMidLwTuVSQziVBy8RcOKGAuMEW2g4XF+g==} + '@objectstack/metadata-protocol@18.1.0': + resolution: {integrity: sha512-z8Wh8lHAZ4zzYBKliqI/o80Wmbr4K4JV+FFnrXznt4CFu9qlRS4UtdD+2Ie3gKrIT1nhgI7A2j0bXPFi6ggz6Q==} engines: {node: '>=22.0.0'} - '@objectstack/metadata@17.7.0': - resolution: {integrity: sha512-mkdXagkRy/ZpJiH15JOvYy/07vz1tLHdGIxv6EmWt8aeAYQyqoVsGdoLi1TY8MmI4tkG0w9qAWt2Myv9JPbZWQ==} + '@objectstack/metadata@18.1.0': + resolution: {integrity: sha512-JzbjTqAsepnXGZ7WrbaUKnF5xpQPYBH8KmfG/oYqm3lOiQ9JGZoXcMEcZHQNnOsj1Bf5wNchVBb52Pv5/PwZ/A==} engines: {node: '>=22.0.0'} - '@objectstack/objectql@17.7.0': - resolution: {integrity: sha512-3TZlfQ1PUdC7+vPzipYHHgEAVIZlpHOB/vchkqoJaLDCuJ+nSfh1YwLfA6NN+Z3MxvNZfmZ1RWtMXsClX0nkeg==} + '@objectstack/objectql@18.1.0': + resolution: {integrity: sha512-Ld5hJSQFojc3oGTdbj2OvoApHxa3K5pqbTgd7PzNiUqQH8V4iirPEe4Cnh22i3VjHpWB9DEA3YJHaYkUrQLo+g==} engines: {node: '>=22.0.0'} - '@objectstack/observability@17.7.0': - resolution: {integrity: sha512-7B5WhLcrW9iekKDikFwnMYxF4b2A8GKH/VxLTDVxaf1JMzzHXxxoq/9+MothmvDZpNWs1l6RuzhumjScKp82cw==} + '@objectstack/observability@18.1.0': + resolution: {integrity: sha512-iwEXlrY0Y4pzxlUKN84A8PjioxQyFQ5PaOghgMpkLh/V5XorUT+W15AIK+BQD0sVmxB2iMZNkcDg8WqNr53/ZA==} engines: {node: '>=22.0.0'} - '@objectstack/platform-objects@17.7.0': - resolution: {integrity: sha512-nYn2NRBj0TWhmdPMIRnWHunaoSKVyzJwzj2eRFcuV0+lEWkF6dTzJp6rTCMUelJ1qNqUKrTyN/8oD+JIaBke/g==} + '@objectstack/platform-objects@18.1.0': + resolution: {integrity: sha512-roQPYsEbiCACebkHtpw70ojvtHl75PCvGpCSCvofz/i7pzlpyPW1uoAxKT3IEwsNjDK7TNmNEe/ghmRGXQMKPA==} engines: {node: '>=22.0.0'} - '@objectstack/plugin-approvals@17.7.0': - resolution: {integrity: sha512-je8T0dTSoTjqE1J140evxfzsvMiSpLoP3DEdu4NDFFJEWJDw/A/rBVHyQbVV0tLpW5j1pysze1myvF2Sc+MXlw==} + '@objectstack/plugin-approvals@18.1.0': + resolution: {integrity: sha512-Z/sD7GxD45PJblGZLs+6wTjckIdEmInsoK+1WWx4E9OtrPlak6Ys887SqnSd+erxBhgdBUVjIGiopCjK5df++w==} - '@objectstack/plugin-audit@17.7.0': - resolution: {integrity: sha512-Z0Wpx4W7oVPUwelmAXUckpABP0m+X9NwborOF/JDEmLOEQdi+2GCaWo2vazMJIx5ItyYLR7RYTvglwHa+ZSLQQ==} + '@objectstack/plugin-audit@18.1.0': + resolution: {integrity: sha512-dumz3qmSL4HapjZumv56YfrfOzZ4cB6j7OQh4TSfFurOt6Pz1GqRhLYIPty3205InLjVeEK7T/MAn2JBwyFUZg==} engines: {node: '>=22.0.0'} - '@objectstack/plugin-auth@17.7.0': - resolution: {integrity: sha512-JUOgGB6Riizw5QEZ5TXutRT1+PpaM/XpXMbtubAjk9dBGiR8NcfVoLoEFNz+Z5O6x82tdD8bcbt7GMDgUZ5wXA==} + '@objectstack/plugin-auth@18.1.0': + resolution: {integrity: sha512-83VSCTlBPwZQ5tIU18F2/U+DbPU6j6V97RLsRSKF21UL3VYRA4/OLkrcZgTzEGVfETkz3r3HJflhYSq4Ed+4ww==} engines: {node: '>=22.0.0'} - '@objectstack/plugin-email@17.7.0': - resolution: {integrity: sha512-58vsGtSv0z6u+W3HqvESIW4OGxbB1/GpR1kY+PdlR0c6JfDc+/YuR0afWm6xrApBoaBPHoMWhtRyoyubgHTJeg==} + '@objectstack/plugin-email@18.1.0': + resolution: {integrity: sha512-Az562+c7GWfYmRqLtBKQq7cDC7FG7/CI/MhtwXo3yN3RKfPlZLugqjw+PboSNPyZQnFBUUwo8F3e4tIk8uZFAQ==} - '@objectstack/plugin-hono-server@17.7.0': - resolution: {integrity: sha512-VunIMCOQjdJE0J9dq4ebXGnUaLcOttvBNdWM74+J+/W8BflmyuHP/o3JkGo6wgP4e8WwwyTQjJ3BxNi4KrjlKA==} + '@objectstack/plugin-hono-server@18.1.0': + resolution: {integrity: sha512-WgBeu5oUtrfQSRZZWMv6aWq3/bgpVSkQFqgrc5bpgE9HsR33X0mb/6JeTfu+gf10MCC9WXNUY5Q1fgbzHrnp7Q==} engines: {node: '>=22.0.0'} - '@objectstack/plugin-pinyin-search@17.7.0': - resolution: {integrity: sha512-2HYOdHNlUULzVS00ENv0EVrCA2yA4AWo3oHlCCJV1NHIqOJwimoIPzSu/FpBa+Z0y9T7cX4VxA4hYQO+/Giy8w==} + '@objectstack/plugin-pinyin-search@18.1.0': + resolution: {integrity: sha512-/FXlBJyQ2fnuXWFMSGSuW9YLvdXRGDGfWGwsWhpPY9czCQ3oOUrQX8qSYecMpUoKEJsT3XIZHSaU/fDoKb6IYg==} - '@objectstack/plugin-security@17.7.0': - resolution: {integrity: sha512-VlUT30iqnD39rAktDCqyAwJbtJTeWkJsFSZ+TFLE9pxcDIJqrCYQJuazn65QcyeQ+xdtkvnkzyPe3JsUz8i4sA==} + '@objectstack/plugin-security@18.1.0': + resolution: {integrity: sha512-V+tGsH6a+8CZY9yUZ6gfq4vH8DvPNNd9YxqJBRtgwlMWb3iFxrh4Y8ACXoMvyuehX3vwt1wldUrpgr1WNbIUxQ==} engines: {node: '>=22.0.0'} - '@objectstack/plugin-sharing@17.7.0': - resolution: {integrity: sha512-oAfwXmP2kQ6mBepPxYxsS6PUmi+XY15LX6kVgVJIwOGT/7iwsY6C4uejWNN7ZwFZ6j3970O43Ia/a73iY3gdbA==} + '@objectstack/plugin-sharing@18.1.0': + resolution: {integrity: sha512-cZI/nmnZ9PcXkdNKBtuM4/Kw9aXG473yL4d+pWqxcLfqG2deG+naT+PnZ6J6mzAXXhYL1MkaBozHikxepYMNKA==} - '@objectstack/plugin-webhooks@17.7.0': - resolution: {integrity: sha512-VxGfFzJldxl5hUn8c8mlE7mrWwNnQJu3M8WPe2F76i5Pp7Hel6Q+yoQU566aechFPQLDkgsJNbVQbwYGAJr/DA==} + '@objectstack/plugin-webhooks@18.1.0': + resolution: {integrity: sha512-x3p8ic4NDJME03REovFC8lUsxM3VtojJnU39ef4YzLRDlcKOuMsjhQIzYWyWVvMqNGXuw40nvls8abr+cDLGqw==} - '@objectstack/rest@17.7.0': - resolution: {integrity: sha512-zCRPpyBAgdIAKEsgdfign2JmalkNCcCI98Kk6xlbnRg06E9NMIn4+D3Q1tsG7DsDtIY3Hao2vZlr6/59yhDy+w==} + '@objectstack/rest@18.1.0': + resolution: {integrity: sha512-/ArsVYM0ua/WS6xCnWsY0ztrs6hfjYTff3Cw/ff3RUjg5ixcmdyFRiqWL0BVAjdEaWPpYDJbpsUX1HjNVqHCZw==} engines: {node: '>=22.0.0'} - '@objectstack/runtime@17.7.0': - resolution: {integrity: sha512-RVyleEk68NXBtdXCFtAarMEbP2JqoG+M9u8GwKERhgyKSfapXSOjfrcBXrAWJDNNXm9r5rrAH6P1Ap1rjUpoeA==} + '@objectstack/runtime@18.1.0': + resolution: {integrity: sha512-EDlekEZg+Al5KDYLv1kgP7URu3RLDBa1HQPu2+XCzOoe310pVe0/cKiwE0tINHk/3aemAMIxidSVpzSUXJ8dHg==} engines: {node: '>=22.0.0'} peerDependencies: - '@objectstack/driver-turso': ^17.7.0 + '@objectstack/driver-turso': ^18.1.0 peerDependenciesMeta: '@objectstack/driver-turso': optional: true - '@objectstack/sdui-parser@17.7.0': - resolution: {integrity: sha512-BaEt37YGK2lBgBXrM+w/tVI+kz5Gz6xhbmCDwgdeGCj8qKALNO8l/oQh/3qdsf6pLIBMfIi+IMqryOICgqwUdw==} + '@objectstack/sdui-parser@18.1.0': + resolution: {integrity: sha512-8Bi5GDDMM862SjXb8tSS+eQUubSKkXUp6aI680Iwjj2GYcB+P9sA2N2Qu8nXyfnBEuaNrixdV7cfr0dDIB1NrA==} - '@objectstack/service-analytics@17.7.0': - resolution: {integrity: sha512-EyVxpQykWckb9vPrxwpSDV9zpWCh5KmqVMPnXflZ0OO9ux4p9dwCFUoFvOzzYW7o3DjVmul7yxKCTY49A9b09w==} + '@objectstack/service-analytics@18.1.0': + resolution: {integrity: sha512-7AKN2pP/MSrLqhDQWalup6C9pct0/SPudbIKTHPFRgZQRSSM2ClB8HuoPLFNXOJpkZRdPIpvBq2NnNRUfuf5Rg==} engines: {node: '>=22.0.0'} - '@objectstack/service-automation@17.7.0': - resolution: {integrity: sha512-AZ1Ob0RrAe+kzjHJcLFUf393EQdgcmrHvW3q1MHU7FcF65CJsDs+9jcI40EE7vPz9kcqw/S/pUNkj+EwrLOfew==} + '@objectstack/service-automation@18.1.0': + resolution: {integrity: sha512-u7jq/Coq3X5JaZiQn0bt0aiqoZvOfjlDQe2/izb0FAvGhXXGqt2KAbcCPkCIpzvKySEahZ3+hHis04DtfmlmjA==} engines: {node: '>=22.0.0'} - '@objectstack/service-cache@17.7.0': - resolution: {integrity: sha512-VP5MJQahN0tdkMuhKL/kfOa/czpZIUJK6n5V5Bmehg4ZEpYzIrzEP3oN0xSUDd8f3KTzHCSz6flKoD2Dtb48rA==} + '@objectstack/service-cache@18.1.0': + resolution: {integrity: sha512-/SOyAJuYDI03weHG5K9BsCAbaZ9qtILs6r4o1RpUhmDrqGcFYxzZ6f9VZ+4IZRbStCtOAUmE+FoWqOcx+uq3SA==} engines: {node: '>=22.0.0'} - '@objectstack/service-cluster@17.7.0': - resolution: {integrity: sha512-btbcHBv9rPX3dB2Y0LqwsCsFCOiyqA8TmdTuv0CtLD/oc92l7JsIfPVP537w5E9i2H3UQ5wm1Epq7k9jCUlf+Q==} + '@objectstack/service-cluster@18.1.0': + resolution: {integrity: sha512-Oov/6iTVcejdgKa963Ud7S48Tdb2iF/L82aBpQndCFe4UEYNxtD6HZWEs08Di7IL7gLhDEkXxbywvXSrCAlNlw==} - '@objectstack/service-datasource@17.7.0': - resolution: {integrity: sha512-SOGSLaKFjXt7N7BPU2H5VAFkuEZ8bt9vvdbkaNDbj9x04oKkdFcaxn8QNluAwwI00W/5quutvim9symwnaIB7g==} + '@objectstack/service-datasource@18.1.0': + resolution: {integrity: sha512-dYvJsc24cTJ31mX5YWxnm92a+35x58EHQYfwp+cPKsmNpjHeMmOvHPk/D4Zos7l+AkyFhEAbjgGwOOmRIlaQqQ==} peerDependencies: - '@objectstack/driver-mongodb': ^17.7.0 - '@objectstack/driver-sqlite-wasm': ^17.7.0 - '@objectstack/driver-turso': ^17.7.0 + '@objectstack/driver-mongodb': 18.1.0 + '@objectstack/driver-sqlite-wasm': 18.1.0 + '@objectstack/driver-turso': ^18.1.0 peerDependenciesMeta: '@objectstack/driver-mongodb': optional: true @@ -540,39 +542,43 @@ packages: '@objectstack/driver-turso': optional: true - '@objectstack/service-i18n@17.7.0': - resolution: {integrity: sha512-A8RAETkXLh7qeni8YROBZbBU4h85i+D6YppUCBAw2MgTUSirTqYCVta/0MC1NTBd3NvZO0HlQk72BpSFzuNQ2g==} + '@objectstack/service-i18n@18.1.0': + resolution: {integrity: sha512-RNfKjfnb5CmQ5pVtejkihjvUl4lM/P5snPbeOT3gSDKhK51W4KxpbqEKo8NtaDgeQEANkZZgKWRmE/X9iEjFkQ==} + engines: {node: '>=22.0.0'} + + '@objectstack/service-job@18.1.0': + resolution: {integrity: sha512-qvG8NegO4kuB1CLqeBdYH4+nZ9YViS7OrBqKgUh54IvOMFZag23rsuJ5Y3xzJfXc1y6CEaSNF1HbE//5eSiZ1w==} engines: {node: '>=22.0.0'} - '@objectstack/service-job@17.7.0': - resolution: {integrity: sha512-NcqnTI+5OrgkfOMtSKlNFcvQJqRxW4SLl7ttRn1HDeedwWnEjFQ8LvfMrGGkACgWHOyyLrnwm+t4QazhzPvWOg==} + '@objectstack/service-mailbox@18.1.0': + resolution: {integrity: sha512-bZk434GbA7c+J56l31qJ8EEGXsdbP79RMTdIhnGuhqGm4rCiWDp5VIBXbGHSYLwVXWyqW5A4qmNHLOLsUqihdQ==} engines: {node: '>=22.0.0'} - '@objectstack/service-messaging@17.7.0': - resolution: {integrity: sha512-p66HSnoBXNeicnTT81dfT4tRsy5fjtSD1jcn+X3pRzEvQf56oFP/wn6eJHjcyjDvZP7zSe6joGIrQKxz6h6tjA==} + '@objectstack/service-messaging@18.1.0': + resolution: {integrity: sha512-yPAHmuxgFN78SBFB1/lvxOu7QcFG7XgNrRZt3Xpg/FP40f3HoEQ5LxD7/UCSIWkF6NpQqhESdiXVU0gcy/hctw==} engines: {node: '>=22.0.0'} - '@objectstack/service-package@17.7.0': - resolution: {integrity: sha512-Jj3hYcs9+fG4bMGvoeKYrixOyY+ZKm92QowV2M9zsT921rUw4kyxgJgq6R+VfFmucEdy0KqTYVdK0jzP5dYiWw==} + '@objectstack/service-package@18.1.0': + resolution: {integrity: sha512-8ZphV/dnYAoLN1szweT+vTXub75dBpFbX5J4voNlFn4VCtktgDKji8/oI/Nlt4fMUDNRz5WpvRxYZ3cKs7VdCg==} engines: {node: '>=22.0.0'} - '@objectstack/service-queue@17.7.0': - resolution: {integrity: sha512-gqt0OZSTJha3XJMYwgSVS5Rfzb2pSRahSN4hc5UBpywXI0zpiFP9H9fzCpv++7eNmy2jSkjSIgIE+xy6FxlU6A==} + '@objectstack/service-queue@18.1.0': + resolution: {integrity: sha512-U+286WDVDgFegOwn1Kb5cuK7ZdRDLAIfKxDNiitbl+c7vExKWwpFIHBCk8nJmljE3mMbwBCS2DE05EhI795T6w==} engines: {node: '>=22.0.0'} - '@objectstack/service-realtime@17.7.0': - resolution: {integrity: sha512-r+0UzUKvGGOxpDb9+dLoLh992uiM60d0q+KCiq/6E3pT/i8T3bfzTn3dSpYxZKQ3bxspXhP0FCaF8s30c0+FFQ==} + '@objectstack/service-realtime@18.1.0': + resolution: {integrity: sha512-zg7CC8NZyIM788308tHUlK6yQjL3s+ZBXn5WV2938qiVy/oatq6/ZBLvRfpTKNKuJ+a9Be+TwqfJ8CdknYj+Cg==} engines: {node: '>=22.0.0'} - '@objectstack/service-settings@17.7.0': - resolution: {integrity: sha512-qv/2OnV8Eq/h/GiUqACvt9UZLuV78zkjdZMPygpZhxTKlsLsA1o30XkdpEQUXTHGzMRW+wJkIkvHYhl/JT0yeA==} + '@objectstack/service-settings@18.1.0': + resolution: {integrity: sha512-rgdCIwkBCQtjCRnlW/DNZGawqo7huapjMI1knZ18mmSLe/zG0fjBKaGop1qiGkbtOZPUJWD4uxtUotisixe+YA==} engines: {node: '>=22.0.0'} - '@objectstack/service-sms@17.7.0': - resolution: {integrity: sha512-LDNB2iHEdiD0th90hzzkPOwaCKkin4zxo0tB5lrNwYJ9mEtkPFR7e7KqNMpCmCHLj6squcdrjQZDPQi9HIEfGg==} + '@objectstack/service-sms@18.1.0': + resolution: {integrity: sha512-Qtmk4/zFPJwCvW0qO9MbIhxdE9AiyYFRjBM22EbAgKfizVp/8iRWDo/5Sl4oeFm3DdVKLS4OxkR4p92G/a8vAw==} - '@objectstack/service-storage@17.7.0': - resolution: {integrity: sha512-G4waSSMDtAq7+ChI0/39xSAUT1AbnN+XkrCZmtLuokcU7MUOA78GOVl1J2nzHiWrY+fOSggqSdVPfOy9oNom+g==} + '@objectstack/service-storage@18.1.0': + resolution: {integrity: sha512-nUaCFOl93TmAirIQkRwv1fwYl5jgdCtWvU+NuUPSoDu2V92/er4JLjIXC08Q5TIwN3dcpw+JO/0q6nLDvRir7w==} engines: {node: '>=22.0.0'} peerDependencies: '@aws-sdk/client-s3': ^3.0.0 @@ -583,12 +589,12 @@ packages: '@aws-sdk/s3-request-presigner': optional: true - '@objectstack/setup@17.7.0': - resolution: {integrity: sha512-8JwpJ0QjDHPpK/0en/hLVnqP4G52VWKg8gNauc2yI5NDr4xJjkkrmrn+q2FWnRB3fSHvzxZ138Sbj6lQj9ikWg==} + '@objectstack/setup@18.1.0': + resolution: {integrity: sha512-1WHZyQ6Gpq2a1AhUxplpThTF37InFQdZpz/n9GCA8zBAU8eHuVBzEHxT3x1rANxkrgAaz0bydFtcKAGLxsj/6g==} engines: {node: '>=22.0.0'} - '@objectstack/spec@17.7.0': - resolution: {integrity: sha512-PmKFK2C+NHiVoCAC+hYifJJWKiiJxZxN/FlpCQKwqPwajPxCcEUk0oVH0Af0yG3AFRQVCXxwIgEHGGeAa8Z4tA==} + '@objectstack/spec@18.1.0': + resolution: {integrity: sha512-Ib4iTFELKyHG+9kfXSyOxMkne1NFF1suo/o/xHVnLv9FQGDUCv7oPOSklBKHOYM3IBiMmxhXfSp1pbkFa5ksAg==} engines: {node: '>=22.0.0'} peerDependencies: ai: ^7.0.0 @@ -596,33 +602,29 @@ packages: ai: optional: true - '@objectstack/trigger-api@17.7.0': - resolution: {integrity: sha512-YiTgJN9ewxfdL0Q1U/bQho1EDfPZpGAEIgzYAFli+keVRaQyKrH7BjKTxW5RKb4rwCplCFwkNO+AsRQHpJ1K3Q==} + '@objectstack/trigger-api@18.1.0': + resolution: {integrity: sha512-8NU/WTB62daZHDetLgFMtPGaCTQjkCpX6OJb231kzQmr6Wt5Saq90JPcQOjWDNUr4hsvcnU082KzURsluUyZCQ==} - '@objectstack/trigger-record-change@17.7.0': - resolution: {integrity: sha512-H7zpibqfOpk8PsmBpKPSkVdyAM/JzxUUakYGY5/Wpg1pMFFx0g0cCREFYcHWCyv2s8YGmwN52gkEsc1QaoaCYw==} + '@objectstack/trigger-record-change@18.1.0': + resolution: {integrity: sha512-6lmzLBk8s9nxU6mwNLraFjcXqBJukyOLlJPShKLKD1tKFQ6nE/1g3dqTtWjJhIIuo7Q/7mHoFrZ424dCW68+yQ==} engines: {node: '>=22.0.0'} - '@objectstack/trigger-schedule@17.7.0': - resolution: {integrity: sha512-eMZgTyXRQk9vrG3p5aY68xW+QN0G+59unsGHwzCzWig7jleOZQlPf48E6WrXAbTEWKYM36bXmC6CU/44bIwG/g==} + '@objectstack/trigger-schedule@18.1.0': + resolution: {integrity: sha512-euvfGFqSU5IhJ9MNU33p2q0q5QnSd1/NKSChC/CuWGfAjAd36EHhv5C3WePPrr+CYzVe3lBY33W+pDrU8iWImw==} engines: {node: '>=22.0.0'} - '@objectstack/types@17.7.0': - resolution: {integrity: sha512-7EIBYXZsZrBMihmmhwfcafI7F8WUvWNRAylJsZ25mBO7okQLhL4J3GX4wYBiyAJUmroIF3R9lCi+noaoWj5E5g==} + '@objectstack/types@18.1.0': + resolution: {integrity: sha512-bM/rI6kLE1cvjWE8KMe2AWwNHE5XAYQC3WuaAyyxV7BXH8D+XukDx9h9O26GBg+z3hhz/gdYhhxelCN127BzgQ==} engines: {node: '>=22.0.0'} - '@objectstack/verify@17.7.0': - resolution: {integrity: sha512-2CG2DlPHSvS9E0Sr0/h3MGG20drHI9XwUomICyYZJU5cKlC97PWarIlzpDdUvkA3/LGARDxX7eZPaDyxsU81Ew==} + '@objectstack/verify@18.1.0': + resolution: {integrity: sha512-sLnGomkPd/4/uQRdDojVhEE1fu/Tv/B76IQWu1ZENOf5kG+QrF7OKS30qBX88Km3Hr0jpFPwDxYuBdgUBi5f8Q==} engines: {node: '>=22.0.0'} '@oclif/core@5.1.6': resolution: {integrity: sha512-uLLRjPykRiTwfxG1HB358F/4QSHX6N1A7hChk9+NiO3hvo/d2nhh/waCmxjSAY+rdc+RV+kDxcOQuuil/tIfQA==} engines: {node: '>=22.0.0'} - '@opentelemetry/semantic-conventions@1.43.0': - resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} - engines: {node: '>=14'} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -718,15 +720,14 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - better-auth@1.7.3: - resolution: {integrity: sha512-8xGp68JQ+l36kniDEgP8bP99TLi1GdEv0NTEUBkyqnYnus/cgFUZseUfqUHKzr2BAsg2O6aD88I9f0U68shanQ==} + better-auth@1.7.12: + resolution: {integrity: sha512-f1fEG0QdTslzbmFA+vOGFFW/XXCWkG4kBxLeH9n1vp1otTH9+ZzV6EJ6797GStWeiYYgS5Vnt9xqE12KwSyAAw==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 - '@sveltejs/kit': ^2.0.0 + '@sveltejs/kit': ^2.0.0 || ^3.0.0 '@tanstack/react-start': ^1.0.0 '@tanstack/solid-start': ^1.0.0 - better-sqlite3: ^12.0.0 drizzle-kit: '>=0.31.4 || >=1.0.0-beta.1' drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 mongodb: ^6.0.0 || ^7.0.0 @@ -738,7 +739,7 @@ packages: react-dom: ^18.0.0 || ^19.0.0 solid-js: ^1.0.0 svelte: ^4.0.0 || ^5.0.0 - vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 vue: ^3.0.0 peerDependenciesMeta: '@lynx-js/react': @@ -751,8 +752,6 @@ packages: optional: true '@tanstack/solid-start': optional: true - better-sqlite3: - optional: true drizzle-kit: optional: true drizzle-orm: @@ -780,8 +779,8 @@ packages: vue: optional: true - better-call@1.4.0: - resolution: {integrity: sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA==} + better-call@1.4.4: + resolution: {integrity: sha512-Gq4XdGFy4dmUxFEAX2nj0ycFcFbD9ZWh8YROouZoyXeF1M11TpYbplk0pDByTFF8Wz+MYa6W9052c9m7dD+mfw==} peerDependencies: zod: ^4.0.0 peerDependenciesMeta: @@ -942,8 +941,8 @@ packages: resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} engines: {node: '>= 10'} - create-objectstack@17.7.0: - resolution: {integrity: sha512-TLrwK23OcDR7K4JpR6FbBK+U5SfQhPyONNea0cgydfcfpjQaBYULmmuhDARvoTsUZGOSuVrVWxSETnKYg2lKvA==} + create-objectstack@18.1.0: + resolution: {integrity: sha512-zJTLiiMZkmqBy4VYU3hDVPWTOrmqkKxRQzinxrkH2fBAYMg8+RbC5nBeG+ei0bQEt4AyWWEjiFeZrkfqMc3bAQ==} engines: {node: '>=22.0.0'} hasBin: true @@ -1177,10 +1176,6 @@ packages: resolution: {integrity: sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==} engines: {node: '>=16.9.0'} - hono@4.13.7: - resolution: {integrity: sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==} - engines: {node: '>=16.9.0'} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1558,6 +1553,10 @@ packages: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} + postal-mime@4.1.4: + resolution: {integrity: sha512-+6Qsn7Q2TSLMScfN4X2VQIclPRKgDAtKhytQex3fVSV/+HgVaawtsQGzKYDQLQIBR7b4Vfg+88WRGvuF/kwLSw==} + engines: {node: '>=20'} + powershell-utils@0.1.0: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} @@ -1910,107 +1909,90 @@ snapshots: escape-html: 1.0.3 xpath: 0.0.32 - '@better-auth/core@1.7.3(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3)': - dependencies: - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@opentelemetry/semantic-conventions': 1.43.0 - '@standard-schema/spec': 1.1.0 - better-call: 1.4.0(zod@4.6.5) - jose: 6.2.12 - kysely: 0.29.5 - nanostores: 1.5.3 - zod: 4.6.5 - - '@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3)': + '@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3)': dependencies: '@better-auth/utils': 0.5.0 - '@better-fetch/fetch': 1.3.1 - '@opentelemetry/semantic-conventions': 1.43.0 + '@better-fetch/fetch': 1.3.2 '@standard-schema/spec': 1.1.0 - better-call: 1.4.0(zod@4.6.5) + better-call: 1.4.4(zod@4.6.5) jose: 6.2.12 kysely: 0.29.5 nanostores: 1.5.3 zod: 4.6.5 - '@better-auth/drizzle-adapter@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/drizzle-adapter@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 - '@better-auth/kysely-adapter@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.5)': + '@better-auth/kysely-adapter@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(kysely@0.29.5)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 optionalDependencies: kysely: 0.29.5 - '@better-auth/memory-adapter@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/memory-adapter@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 - '@better-auth/mongo-adapter@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(mongodb@7.6.0)': + '@better-auth/mongo-adapter@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(mongodb@7.6.0)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 optionalDependencies: mongodb: 7.6.0 - '@better-auth/oauth-provider@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5))': + '@better-auth/oauth-provider@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5))': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) '@better-auth/utils': 0.5.0 - '@better-fetch/fetch': 1.3.1 - better-auth: 1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0) - better-call: 1.4.0(zod@4.6.5) + '@better-fetch/fetch': 1.3.2 + better-auth: 1.7.12(mongodb@7.6.0) + better-call: 1.4.4(zod@4.6.5) jose: 6.2.12 zod: 4.6.5 - '@better-auth/prisma-adapter@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/prisma-adapter@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 - '@better-auth/scim@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5))': + '@better-auth/scim@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5))': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) '@better-auth/utils': 0.5.0 '@noble/hashes': 2.4.0 - better-auth: 1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0) - better-call: 1.4.0(zod@4.6.5) + better-auth: 1.7.12(mongodb@7.6.0) + better-call: 1.4.4(zod@4.6.5) zod: 4.6.5 - '@better-auth/sso@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5))': + '@better-auth/sso@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5))': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) '@better-auth/utils': 0.5.0 - '@better-fetch/fetch': 1.3.1 + '@better-fetch/fetch': 1.3.2 '@xmldom/xmldom': 0.9.12 - better-auth: 1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0) - better-call: 1.4.0(zod@4.6.5) + better-auth: 1.7.12(mongodb@7.6.0) + better-call: 1.4.4(zod@4.6.5) fast-xml-parser: 5.11.1 jose: 6.2.12 samlify: 2.13.1 tldts: 7.4.12 zod: 4.6.5 - '@better-auth/telemetry@1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - - '@better-auth/utils@0.4.2': - dependencies: - '@noble/hashes': 2.4.0 + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.2 '@better-auth/utils@0.5.0': dependencies: '@noble/hashes': 2.4.0 - '@better-fetch/fetch@1.3.1': {} + '@better-fetch/fetch@1.3.2': {} '@esbuild/aix-ppc64@0.28.2': optional: true @@ -2113,10 +2095,6 @@ snapshots: dependencies: hono: 4.13.13 - '@hono/node-server@2.1.1(hono@4.13.7)': - dependencies: - hono: 4.13.7 - '@jitl/quickjs-ffi-types@0.32.0': {} '@jitl/quickjs-wasmfile-debug-asyncify@0.32.0': @@ -2153,7 +2131,7 @@ snapshots: '@modelcontextprotocol/sdk@1.32.1(zod@4.6.5)': dependencies: - '@hono/node-server': 2.1.1(hono@4.13.7) + '@hono/node-server': 2.1.1(hono@4.13.13) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -2163,7 +2141,7 @@ snapshots: eventsource-parser: 3.1.1 express: 5.2.1 express-rate-limit: 8.7.0(express@5.2.1) - hono: 4.13.7 + hono: 4.13.13 jose: 6.2.12 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -2183,69 +2161,69 @@ snapshots: '@nodable/entities@3.0.0': {} - '@objectstack/account@17.7.0': + '@objectstack/account@18.1.0': dependencies: - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/cli@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': - dependencies: - '@objectstack/account': 17.7.0 - '@objectstack/client': 17.7.0 - '@objectstack/cloud-connection': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/console': 17.7.0 - '@objectstack/core': 17.7.0 - '@objectstack/driver-memory': 17.7.0 - '@objectstack/driver-mongodb': 17.7.0 - '@objectstack/driver-sql': 17.7.0 - '@objectstack/driver-sqlite-wasm': 17.7.0(better-sqlite3@13.0.3) - '@objectstack/formula': 17.7.0 - '@objectstack/lint': 17.7.0 - '@objectstack/mcp': 17.7.0 - '@objectstack/metadata': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-protocol': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/plugin-approvals': 17.7.0 - '@objectstack/plugin-audit': 17.7.0 - '@objectstack/plugin-auth': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/plugin-email': 17.7.0 - '@objectstack/plugin-hono-server': 17.7.0 - '@objectstack/plugin-pinyin-search': 17.7.0 - '@objectstack/plugin-security': 17.7.0 - '@objectstack/plugin-sharing': 17.7.0 - '@objectstack/plugin-webhooks': 17.7.0 - '@objectstack/rest': 17.7.0 - '@objectstack/runtime': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/service-analytics': 17.7.0 - '@objectstack/service-automation': 17.7.0 - '@objectstack/service-cache': 17.7.0 - '@objectstack/service-datasource': 17.7.0(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3)) - '@objectstack/service-job': 17.7.0 - '@objectstack/service-messaging': 17.7.0 - '@objectstack/service-package': 17.7.0 - '@objectstack/service-queue': 17.7.0 - '@objectstack/service-realtime': 17.7.0 - '@objectstack/service-settings': 17.7.0 - '@objectstack/service-sms': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/service-storage': 17.7.0 - '@objectstack/setup': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/trigger-api': 17.7.0 - '@objectstack/trigger-record-change': 17.7.0 - '@objectstack/trigger-schedule': 17.7.0 - '@objectstack/types': 17.7.0 - '@objectstack/verify': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3))(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/cli@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + dependencies: + '@objectstack/account': 18.1.0 + '@objectstack/client': 18.1.0 + '@objectstack/cloud-connection': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/console': 18.1.0 + '@objectstack/core': 18.1.0 + '@objectstack/driver-memory': 18.1.0 + '@objectstack/driver-mongodb': 18.1.0 + '@objectstack/driver-sql': 18.1.0 + '@objectstack/driver-sqlite-wasm': 18.1.0(better-sqlite3@13.0.3) + '@objectstack/formula': 18.1.0 + '@objectstack/lint': 18.1.0 + '@objectstack/mcp': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-protocol': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/plugin-approvals': 18.1.0 + '@objectstack/plugin-audit': 18.1.0 + '@objectstack/plugin-auth': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/plugin-email': 18.1.0 + '@objectstack/plugin-hono-server': 18.1.0 + '@objectstack/plugin-pinyin-search': 18.1.0 + '@objectstack/plugin-security': 18.1.0 + '@objectstack/plugin-sharing': 18.1.0 + '@objectstack/plugin-webhooks': 18.1.0 + '@objectstack/rest': 18.1.0 + '@objectstack/runtime': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/service-analytics': 18.1.0 + '@objectstack/service-automation': 18.1.0 + '@objectstack/service-cache': 18.1.0 + '@objectstack/service-datasource': 18.1.0(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0) + '@objectstack/service-job': 18.1.0 + '@objectstack/service-mailbox': 18.1.0 + '@objectstack/service-messaging': 18.1.0 + '@objectstack/service-package': 18.1.0 + '@objectstack/service-queue': 18.1.0 + '@objectstack/service-realtime': 18.1.0 + '@objectstack/service-settings': 18.1.0 + '@objectstack/service-sms': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/service-storage': 18.1.0 + '@objectstack/setup': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/trigger-api': 18.1.0 + '@objectstack/trigger-record-change': 18.1.0 + '@objectstack/trigger-schedule': 18.1.0 + '@objectstack/types': 18.1.0 + '@objectstack/verify': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) '@oclif/core': 5.1.6 bundle-require: 5.1.0(esbuild@0.28.2) chalk: 6.0.1 chokidar: 5.0.0 - create-objectstack: 17.7.0 + create-objectstack: 18.1.0 dotenv-flow: 4.1.0 esbuild: 0.28.2 ts-morph: 28.0.0 @@ -2261,7 +2239,6 @@ snapshots: - '@better-auth/utils' - '@better-fetch/fetch' - '@cfworker/json-schema' - - '@cloudflare/workers-types' - '@lynx-js/react' - '@mongodb-js/zstd' - '@opentelemetry/api' @@ -2299,26 +2276,26 @@ snapshots: - vitest - vue - '@objectstack/client@17.7.0': + '@objectstack/client@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/cloud-connection@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + '@objectstack/cloud-connection@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-protocol': 17.7.0 - '@objectstack/runtime': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-protocol': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/runtime': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@better-auth/utils' - '@better-fetch/fetch' - - '@cloudflare/workers-types' - '@lynx-js/react' - '@mongodb-js/zstd' - '@objectstack/driver-turso' @@ -2358,30 +2335,32 @@ snapshots: - vitest - vue - '@objectstack/console@17.7.0': {} + '@objectstack/connector-imap@18.1.0': {} + + '@objectstack/console@18.1.0': {} - '@objectstack/core@17.7.0': + '@objectstack/core@18.1.0': dependencies: - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 zod: 4.6.5 transitivePeerDependencies: - ai - '@objectstack/driver-memory@17.7.0': + '@objectstack/driver-memory@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 mingo: 7.2.4 transitivePeerDependencies: - ai - '@objectstack/driver-mongodb@17.7.0': + '@objectstack/driver-mongodb@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 mongodb: 7.6.0 nanoid: 6.0.1 transitivePeerDependencies: @@ -2394,12 +2373,12 @@ snapshots: - snappy - socks - '@objectstack/driver-sql@17.7.0': + '@objectstack/driver-sql@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 knex: 3.3.0(better-sqlite3@13.0.3) nanoid: 6.0.1 optionalDependencies: @@ -2413,11 +2392,11 @@ snapshots: - sqlite3 - supports-color - '@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3)': + '@objectstack/driver-sqlite-wasm@18.1.0(better-sqlite3@13.0.3)': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/driver-sql': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/driver-sql': 18.1.0 + '@objectstack/spec': 18.1.0 knex: 3.3.0(better-sqlite3@13.0.3) nanoid: 6.0.1 sql.js: 1.14.2 @@ -2434,18 +2413,18 @@ snapshots: - supports-color - tedious - '@objectstack/formula@17.7.0': + '@objectstack/formula@18.1.0': dependencies: '@marcbachmann/cel-js': 8.0.0 - '@objectstack/spec': 17.7.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/lint@17.7.0': + '@objectstack/lint@18.1.0': dependencies: - '@objectstack/formula': 17.7.0 - '@objectstack/sdui-parser': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/formula': 18.1.0 + '@objectstack/sdui-parser': 18.1.0 + '@objectstack/spec': 18.1.0 ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) sucrase: 3.35.1 @@ -2453,137 +2432,129 @@ snapshots: transitivePeerDependencies: - ai - '@objectstack/mcp@17.7.0': + '@objectstack/mcp@18.1.0': dependencies: '@modelcontextprotocol/sdk': 1.32.1(zod@4.6.5) - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 zod: 4.6.5 transitivePeerDependencies: - '@cfworker/json-schema' - ai - supports-color - '@objectstack/metadata-core@17.7.0': + '@objectstack/metadata-core@18.1.0': dependencies: - '@objectstack/spec': 17.7.0 + '@objectstack/spec': 18.1.0 zod: 4.6.5 transitivePeerDependencies: - ai - '@objectstack/metadata-fs@17.7.0': + '@objectstack/metadata-fs@18.1.0': dependencies: - '@objectstack/metadata-core': 17.7.0 + '@objectstack/metadata-core': 18.1.0 chokidar: 5.0.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/metadata-protocol@17.7.0': + '@objectstack/metadata-protocol@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/lint': 17.7.0 - '@objectstack/metadata': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/sdui-parser': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/lint': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/sdui-parser': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 zod: 4.6.5 transitivePeerDependencies: - ai - - vitest - '@objectstack/metadata@17.7.0': + '@objectstack/metadata@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-fs': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-fs': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 chokidar: 5.0.0 glob: 13.0.6 js-yaml: 5.4.1 zod: 4.6.5 transitivePeerDependencies: - ai - - vitest - '@objectstack/objectql@17.7.0': + '@objectstack/objectql@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/metadata': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-protocol': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-protocol': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) zod: 4.6.5 transitivePeerDependencies: - ai - - vitest - '@objectstack/observability@17.7.0': + '@objectstack/observability@18.1.0': dependencies: - '@objectstack/spec': 17.7.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/platform-objects@17.7.0': + '@objectstack/platform-objects@18.1.0': dependencies: - '@objectstack/metadata-core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-approvals@17.7.0': + '@objectstack/plugin-approvals@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-audit@17.7.0': + '@objectstack/plugin-audit@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-auth@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + '@objectstack/plugin-auth@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/oauth-provider': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5)) - '@better-auth/scim': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5)) - '@better-auth/sso': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0))(better-call@1.4.0(zod@4.6.5)) + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/oauth-provider': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5)) + '@better-auth/scim': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5)) + '@better-auth/sso': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(mongodb@7.6.0))(better-call@1.4.4(zod@4.6.5)) '@noble/hashes': 2.4.0 - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/rest': 17.7.0 - '@objectstack/service-messaging': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 - better-auth: 1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0) + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/rest': 18.1.0 + '@objectstack/service-messaging': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 + better-auth: 1.7.12(mongodb@7.6.0) jose: 6.2.12 transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' - - '@cloudflare/workers-types' - '@lynx-js/react' - '@opentelemetry/api' - '@prisma/client' @@ -2592,7 +2563,6 @@ snapshots: - '@tanstack/solid-start' - ai - better-call - - better-sqlite3 - drizzle-kit - drizzle-orm - kysely @@ -2609,118 +2579,113 @@ snapshots: - vitest - vue - '@objectstack/plugin-email@17.7.0': + '@objectstack/plugin-email@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 nodemailer: 10.0.16 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-hono-server@17.7.0': + '@objectstack/plugin-hono-server@18.1.0': dependencies: '@hono/node-server': 2.1.1(hono@4.13.13) - '@objectstack/core': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 hono: 4.13.13 transitivePeerDependencies: - ai - '@objectstack/plugin-pinyin-search@17.7.0': + '@objectstack/plugin-pinyin-search@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/types': 18.1.0 pinyin-pro: 3.29.4 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-security@17.7.0': + '@objectstack/plugin-security@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-sharing@17.7.0': + '@objectstack/plugin-sharing@18.1.0': dependencies: '@noble/hashes': 2.4.0 - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/plugin-webhooks@17.7.0': + '@objectstack/plugin-webhooks@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/service-messaging': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/service-messaging': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/rest@17.7.0': + '@objectstack/rest@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/service-package': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/service-package': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 exceljs: 4.4.0 zod: 4.6.5 transitivePeerDependencies: - ai - - vitest - '@objectstack/runtime@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': - dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/driver-memory': 17.7.0 - '@objectstack/driver-sql': 17.7.0 - '@objectstack/driver-sqlite-wasm': 17.7.0(better-sqlite3@13.0.3) - '@objectstack/formula': 17.7.0 - '@objectstack/metadata': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-protocol': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/plugin-auth': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/plugin-security': 17.7.0 - '@objectstack/rest': 17.7.0 - '@objectstack/service-cluster': 17.7.0 - '@objectstack/service-datasource': 17.7.0(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3)) - '@objectstack/service-i18n': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/runtime@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + dependencies: + '@objectstack/core': 18.1.0 + '@objectstack/driver-memory': 18.1.0 + '@objectstack/driver-sql': 18.1.0 + '@objectstack/driver-sqlite-wasm': 18.1.0(better-sqlite3@13.0.3) + '@objectstack/formula': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-protocol': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/plugin-auth': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/plugin-security': 18.1.0 + '@objectstack/rest': 18.1.0 + '@objectstack/service-cluster': 18.1.0 + '@objectstack/service-datasource': 18.1.0(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0) + '@objectstack/service-i18n': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 quickjs-emscripten: 0.32.0 zod: 4.6.5 optionalDependencies: - '@objectstack/driver-mongodb': 17.7.0 + '@objectstack/driver-mongodb': 18.1.0 transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@better-auth/utils' - '@better-fetch/fetch' - - '@cloudflare/workers-types' - '@lynx-js/react' - '@mongodb-js/zstd' - '@opentelemetry/api' @@ -2759,56 +2724,55 @@ snapshots: - vitest - vue - '@objectstack/sdui-parser@17.7.0': {} + '@objectstack/sdui-parser@18.1.0': {} - '@objectstack/service-analytics@17.7.0': + '@objectstack/service-analytics@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/service-automation@17.7.0': + '@objectstack/service-automation@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/formula': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/metadata-protocol': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/formula': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/metadata-protocol': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-cache@17.7.0': + '@objectstack/service-cache@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/service-cluster@17.7.0': + '@objectstack/service-cluster@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/service-datasource@17.7.0(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3))': + '@objectstack/service-datasource@18.1.0(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0)': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/driver-memory': 17.7.0 - '@objectstack/driver-sql': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/driver-memory': 18.1.0 + '@objectstack/driver-sql': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 pg-connection-string: 2.14.0 optionalDependencies: - '@objectstack/driver-mongodb': 17.7.0 - '@objectstack/driver-sqlite-wasm': 17.7.0(better-sqlite3@13.0.3) + '@objectstack/driver-mongodb': 18.1.0 + '@objectstack/driver-sqlite-wasm': 18.1.0(better-sqlite3@13.0.3) transitivePeerDependencies: - ai - mariadb @@ -2820,83 +2784,86 @@ snapshots: - sqlite3 - supports-color - tedious - - vitest - '@objectstack/service-i18n@17.7.0': + '@objectstack/service-i18n@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/service-job@17.7.0': + '@objectstack/service-job@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 croner: 10.0.1 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-messaging@17.7.0': + '@objectstack/service-mailbox@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/connector-imap': 18.1.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 + postal-mime: 4.1.4 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-package@17.7.0': + '@objectstack/service-messaging@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-queue@17.7.0': + '@objectstack/service-package@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-realtime@17.7.0': + '@objectstack/service-queue@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + transitivePeerDependencies: + - ai + + '@objectstack/service-realtime@18.1.0': + dependencies: + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-settings@17.7.0': + '@objectstack/service-settings@18.1.0': dependencies: '@noble/ciphers': 2.4.0 - '@objectstack/core': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/service-sms@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + '@objectstack/service-sms@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/plugin-auth': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/plugin-auth': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - '@better-auth/utils' - '@better-fetch/fetch' - - '@cloudflare/workers-types' - '@lynx-js/react' - '@opentelemetry/api' - '@prisma/client' @@ -2905,7 +2872,6 @@ snapshots: - '@tanstack/solid-start' - ai - better-call - - better-sqlite3 - drizzle-kit - drizzle-orm - kysely @@ -2922,83 +2888,101 @@ snapshots: - vitest - vue - '@objectstack/service-storage@17.7.0': + '@objectstack/service-storage@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/observability': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/observability': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/setup@17.7.0': + '@objectstack/setup@18.1.0': dependencies: - '@objectstack/platform-objects': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - - vitest - '@objectstack/spec@17.7.0': + '@objectstack/spec@18.1.0': dependencies: pg-connection-string: 2.14.0 zod: 4.6.5 - '@objectstack/trigger-api@17.7.0': + '@objectstack/trigger-api@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/trigger-record-change@17.7.0': + '@objectstack/trigger-record-change@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/spec': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/trigger-schedule@17.7.0': + '@objectstack/trigger-schedule@18.1.0': dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/metadata-core': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/core': 18.1.0 + '@objectstack/metadata-core': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/types': 18.1.0 croner: 10.0.1 transitivePeerDependencies: - ai - - vitest - '@objectstack/types@17.7.0': + '@objectstack/types@18.1.0': dependencies: - '@objectstack/spec': 17.7.0 + '@objectstack/spec': 18.1.0 transitivePeerDependencies: - ai - '@objectstack/verify@17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3))(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': - dependencies: - '@objectstack/core': 17.7.0 - '@objectstack/objectql': 17.7.0 - '@objectstack/platform-objects': 17.7.0 - '@objectstack/plugin-auth': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/plugin-hono-server': 17.7.0 - '@objectstack/plugin-security': 17.7.0 - '@objectstack/plugin-sharing': 17.7.0 - '@objectstack/rest': 17.7.0 - '@objectstack/runtime': 17.7.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) - '@objectstack/service-analytics': 17.7.0 - '@objectstack/service-automation': 17.7.0 - '@objectstack/service-datasource': 17.7.0(@objectstack/driver-mongodb@17.7.0)(@objectstack/driver-sqlite-wasm@17.7.0(better-sqlite3@13.0.3)) - '@objectstack/service-settings': 17.7.0 - '@objectstack/spec': 17.7.0 - '@objectstack/types': 17.7.0 + '@objectstack/verify@18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3)': + dependencies: + '@objectstack/core': 18.1.0 + '@objectstack/mcp': 18.1.0 + '@objectstack/metadata': 18.1.0 + '@objectstack/objectql': 18.1.0 + '@objectstack/platform-objects': 18.1.0 + '@objectstack/plugin-approvals': 18.1.0 + '@objectstack/plugin-audit': 18.1.0 + '@objectstack/plugin-auth': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/plugin-email': 18.1.0 + '@objectstack/plugin-hono-server': 18.1.0 + '@objectstack/plugin-pinyin-search': 18.1.0 + '@objectstack/plugin-security': 18.1.0 + '@objectstack/plugin-sharing': 18.1.0 + '@objectstack/plugin-webhooks': 18.1.0 + '@objectstack/rest': 18.1.0 + '@objectstack/runtime': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(better-sqlite3@13.0.3)(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/service-analytics': 18.1.0 + '@objectstack/service-automation': 18.1.0 + '@objectstack/service-cache': 18.1.0 + '@objectstack/service-datasource': 18.1.0(@objectstack/driver-mongodb@18.1.0)(@objectstack/driver-sqlite-wasm@18.1.0) + '@objectstack/service-job': 18.1.0 + '@objectstack/service-mailbox': 18.1.0 + '@objectstack/service-messaging': 18.1.0 + '@objectstack/service-package': 18.1.0 + '@objectstack/service-queue': 18.1.0 + '@objectstack/service-realtime': 18.1.0 + '@objectstack/service-settings': 18.1.0 + '@objectstack/service-sms': 18.1.0(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(kysely@0.29.5)(mongodb@7.6.0)(nanostores@1.5.3) + '@objectstack/service-storage': 18.1.0 + '@objectstack/spec': 18.1.0 + '@objectstack/trigger-api': 18.1.0 + '@objectstack/trigger-record-change': 18.1.0 + '@objectstack/trigger-schedule': 18.1.0 + '@objectstack/types': 18.1.0 transitivePeerDependencies: + - '@aws-sdk/client-s3' - '@aws-sdk/credential-providers' + - '@aws-sdk/s3-request-presigner' - '@better-auth/utils' - '@better-fetch/fetch' - - '@cloudflare/workers-types' + - '@cfworker/json-schema' - '@lynx-js/react' - '@mongodb-js/zstd' - '@objectstack/driver-mongodb' @@ -3061,8 +3045,6 @@ snapshots: wrap-ansi: 7.0.0 wsl-utils: 0.4.0 - '@opentelemetry/semantic-conventions@1.43.0': {} - '@standard-schema/spec@1.1.0': {} '@ts-morph/common@0.29.0': @@ -3167,36 +3149,34 @@ snapshots: base64-js@1.5.1: {} - better-auth@1.7.3(better-sqlite3@13.0.3)(mongodb@7.6.0): - dependencies: - '@better-auth/core': 1.7.3(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/drizzle-adapter': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/kysely-adapter': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.5) - '@better-auth/memory-adapter': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(mongodb@7.6.0) - '@better-auth/prisma-adapter': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/telemetry': 1.7.3(@better-auth/core@1.7.3(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) - '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 + better-auth@1.7.12(mongodb@7.6.0): + dependencies: + '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/drizzle-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0) + '@better-auth/kysely-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(kysely@0.29.5) + '@better-auth/memory-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0) + '@better-auth/mongo-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(mongodb@7.6.0) + '@better-auth/prisma-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0) + '@better-auth/telemetry': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-call@1.4.4(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2) + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.2 '@noble/ciphers': 2.4.0 '@noble/hashes': 2.4.0 - better-call: 1.4.0(zod@4.6.5) + better-call: 1.4.4(zod@4.6.5) defu: 6.1.7 jose: 6.2.12 kysely: 0.29.5 nanostores: 1.5.3 zod: 4.6.5 optionalDependencies: - better-sqlite3: 13.0.3 mongodb: 7.6.0 transitivePeerDependencies: - - '@cloudflare/workers-types' - '@opentelemetry/api' - better-call@1.4.0(zod@4.6.5): + better-call@1.4.4(zod@4.6.5): dependencies: '@better-auth/utils': 0.5.0 - '@better-fetch/fetch': 1.3.1 + '@better-fetch/fetch': 1.3.2 rou3: 0.9.2 set-cookie-parser: 3.1.2 optionalDependencies: @@ -3344,7 +3324,7 @@ snapshots: crc-32: 1.2.2 readable-stream: 3.6.2 - create-objectstack@17.7.0: + create-objectstack@18.1.0: dependencies: chalk: 6.0.1 commander: 15.0.0 @@ -3616,8 +3596,6 @@ snapshots: hono@4.13.13: {} - hono@4.13.7: {} - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -3880,6 +3858,8 @@ snapshots: pkce-challenge@5.0.1: {} + postal-mime@4.1.4: {} + powershell-utils@0.1.0: {} process-nextick-args@2.0.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2bb641d..9f3ced3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -76,3 +76,16 @@ peerDependencyRules: '@better-auth/oauth-provider>@better-auth/utils': '0.5.0' '@better-auth/scim>@better-auth/utils': '0.5.0' '@better-auth/sso>@better-auth/utils': '0.5.0' + +# An exact pin does not hold the packages @objectstack/cli itself ranges (18.1.0 +# upgrade checklist, step 1): without this block the tree resolves a mix of +# 18.x versions. Move these with the @objectstack/* pins at every upgrade. +overrides: + '@objectstack/core': '18.1.0' + '@objectstack/driver-memory': '18.1.0' + '@objectstack/driver-mongodb': '18.1.0' + '@objectstack/driver-sql': '18.1.0' + '@objectstack/driver-sqlite-wasm': '18.1.0' + '@objectstack/objectql': '18.1.0' + '@objectstack/observability': '18.1.0' + '@objectstack/runtime': '18.1.0' diff --git a/src/apps/clm.app.ts b/src/apps/clm.app.ts index 6af3e39..7a776d4 100644 --- a/src/apps/clm.app.ts +++ b/src/apps/clm.app.ts @@ -246,9 +246,10 @@ export const ClmApp = App.create({ * `systemPermissions` are `clm_admin.access` plus six action gates, so an * administrator does NOT hold `clm_legal.access`. The one capability every * CLM audience shares is `clm_requester.access`, because - * `bind-position-sets.ts` binds `RequesterSet` to *every* position: + * every position names `RequesterSet` in its `permissionSets` + * (`src/sharing/positions.ts`): * - * ...Object.values(CLM_POSITION).map((position) => [position, RequesterSet.name]) + * permissionSets: [LegalSet.name, RequesterSet.name] * * PR #25's audience matrix is the measurement: 我的合同, gated on exactly * this capability, is served to legal, finance, records, clm_admin and an diff --git a/src/flows/_daily-sweep.ts b/src/flows/_daily-sweep.ts index ea9c548..79298f2 100644 --- a/src/flows/_daily-sweep.ts +++ b/src/flows/_daily-sweep.ts @@ -1,4 +1,6 @@ +import type { EvaluatedExpression } from '@objectstack/spec'; import type { Flow, FlowNode, FlowEdge } from '@objectstack/spec/automation'; +import { must, ref } from './_values.js'; /** * The shape the six daily jobs of DESIGN.md §06 share — F3, F4, F10, F11, F12 @@ -142,7 +144,7 @@ export function sweepFlow(spec: { type: 'loop', label: stage.label, config: { - collection: `{${stage.id}Rows}`, + collection: must(`${stage.id}Rows`), iteratorVariable: stage.item, maxIterations: SWEEP_LIMIT, body: { @@ -203,10 +205,10 @@ export function sweepFlow(spec: { * legal owner, transcribed rather than shared because a flow function and a * lowered hook body cannot import each other. * - * Returns a LIST. A notify node's `recipients` interpolates a single `{var}` - * token to the value itself, so `recipients: '{holders.userIds}'` fans out - * correctly for any number of holders — whereas an ARRAY of templates would - * stringify a nested list into one bogus comma-joined recipient. + * Returns a LIST. A notify node's `recipients` is a value slot, so one CEL + * read of the list — `recipients: ref('holders.userIds')` — fans out + * correctly for any number of holders, where an ARRAY of reads would nest the + * list inside another. */ export function positionHolders({ input }: { input: Record }): { userIds: string[]; @@ -302,7 +304,7 @@ export function holdersPrelude(position: string, variable: string): FlowNode[] { label: `${position} — user ids`, config: { function: 'clm_position_holders', - inputs: { rows: `{${variable}Rows}` }, + inputs: { rows: ref(`${variable}Rows`) }, outputVariable: variable, }, }, @@ -316,7 +318,7 @@ export interface RowNotice { readonly title: string; readonly message: string; readonly sourceObject: string; - readonly sourceId: string; + readonly sourceId: EvaluatedExpression; readonly channels?: readonly string[]; } @@ -340,7 +342,7 @@ export interface RowNotice { export function rowBody(spec: { readonly id: string; readonly lead?: readonly FlowNode[]; - readonly recipients?: { readonly primary?: string; readonly also?: string; readonly copy?: string }; + readonly recipients?: { readonly primary?: EvaluatedExpression; readonly also?: EvaluatedExpression; readonly copy?: EvaluatedExpression }; readonly notice?: RowNotice; }): { nodes: FlowNode[]; edges: FlowEdge[] } { const nodes: FlowNode[] = [...(spec.lead ?? [])]; @@ -388,10 +390,9 @@ export function rowBody(spec: { type: 'notify', label: 'Notify', config: { - // ONE template resolving to the whole list. An array of templates - // would stringify a nested list into a single comma-joined recipient; - // a single `{var}` token interpolates to the value itself. - recipients: '{recipients.userIds}', + // ONE read resolving to the whole list. An array of reads would nest + // the list inside another instead of handing over its ids. + recipients: ref('recipients.userIds'), channels: [...(spec.notice.channels ?? ['inbox', 'email'])], severity: spec.notice.severity, topic: spec.notice.topic, diff --git a/src/flows/_values.ts b/src/flows/_values.ts new file mode 100644 index 0000000..32345b2 --- /dev/null +++ b/src/flows/_values.ts @@ -0,0 +1,54 @@ +import { expression } from '@objectstack/spec'; + +/** + * The two ways a flow node's VALUE slot reads a run variable, since protocol 18 + * (ADR-0032: one expression dialect per slot). + * + * Up to 17.x every value slot — a CRUD node's `fields` and `filter`, a script's + * `inputs`, a notify node's `recipients` and `sourceId`, a loop's `collection`, + * a screen's defaults — read the single-brace `{path}` template, and a token + * that resolved to nothing wrote nothing. 18 refuses that dialect in a value + * slot: the slot takes a CEL value envelope, and CEL fails the run on a + * variable or key that is not there instead of writing nothing. These two + * helpers are the two answers to "what if it is not there", chosen per slot so + * that each slot behaves as it did on 17.x. + * + * Text slots (a notify node's `title` / `message`, a screen's `description`) + * are not values: they take `{{ path }}` holes, written in place. And a date + * macro in a CRUD filter — `'{today}'`, `'{tomorrow}'`, `'{30_days_ago}'` — is + * a query placeholder the ObjectQL read path resolves, not a run variable: it + * stays a plain string, and wrapping it in either helper refuses the node. + */ + +/** + * A guarded read: `null` where any step of `path` is unbound or null — the + * nearest CEL has to the template's "wrote nothing". The shape is the one + * `os validate` prescribes, `has()` a step at a time from `vars`, which holds + * every variable the run has bound (loop iterators and script outputs + * included). + * + * Use it for every slot where an absent value is legitimate: an optional + * lookup (`legal_owner` is only assigned when the type needs legal review), a + * screen field the user left empty, a script input the function tolerates + * missing. + */ +export function ref(path: string) { + const steps = path.split('.'); + const guards: string[] = []; + for (let i = 1; i <= steps.length; i += 1) { + const at = `vars.${steps.slice(0, i).join('.')}`; + guards.push(`has(${at})`); + if (i < steps.length) guards.push(`${at} != null`); + } + return expression(`${guards.join(' && ')} ? vars.${path} : null`); +} + +/** + * An unguarded read, for a CRUD node's `filter` operand. An operand that is + * absent or `null` refuses the node — an absent condition would widen the + * query, not narrow it — which is what a `{path}` token that resolved to + * nothing did on 17.x, so a guard would buy nothing here. + */ +export function must(path: string) { + return expression(path); +} diff --git a/src/flows/contract-approval.flow.ts b/src/flows/contract-approval.flow.ts index 5610ae1..223e1a0 100644 --- a/src/flows/contract-approval.flow.ts +++ b/src/flows/contract-approval.flow.ts @@ -1,6 +1,7 @@ import { P, expression } from '@objectstack/spec'; import type { Flow } from '@objectstack/spec/automation'; import { CLM_POSITION } from '../sharing/positions.js'; +import { must, ref } from './_values.js'; /** * F5 `contract_approval` — the approval ladder (DESIGN.md §06 F5, §03 状态机, @@ -247,7 +248,7 @@ export const ContractApprovalFlow: Flow = { id: 'get_contract', type: 'get_record', label: 'Get Contract', - config: { objectName: 'clm_contract', filter: { id: '{record.id}' }, outputVariable: 'contractRecord' }, + config: { objectName: 'clm_contract', filter: { id: must('record.id') }, outputVariable: 'contractRecord' }, }, { id: 'normalize_routing', @@ -256,10 +257,10 @@ export const ContractApprovalFlow: Flow = { config: { function: 'clm_route_flags', inputs: { - legal: '{contractRecord.route_legal_head}', - finance: '{contractRecord.route_finance}', - executive: '{contractRecord.route_executive}', - gm: '{contractRecord.route_gm}', + legal: ref('contractRecord.route_legal_head'), + finance: ref('contractRecord.route_finance'), + executive: ref('contractRecord.route_executive'), + gm: ref('contractRecord.route_gm'), }, outputVariable: 'route', }, @@ -361,7 +362,7 @@ export const ContractApprovalFlow: Flow = { label: 'Mark Approved', config: { objectName: 'clm_contract', - filter: { id: '{record.id}' }, + filter: { id: must('record.id') }, // `status` only. `approved_at` is stamped by the state machine on this // very edge (`contract.hook.ts`: `from === 'in_approval' && to === // 'approved'` ⇒ `input.approved_at = now`), so writing it here would @@ -379,13 +380,13 @@ export const ContractApprovalFlow: Flow = { // `owner_id` is the platform ownership anchor and the security // middleware stamps the acting user on any insert that leaves it // empty, so the business owner is always someone. - recipients: '{contractRecord.owner_id}', + recipients: ref('contractRecord.owner_id'), channels: ['inbox', 'email'], topic: 'clm_contract_approved', - title: 'Contract approved: {contractRecord.title}', - message: 'Your contract {contractRecord.title} has cleared every approval rung. It can now go out for signature.', + title: 'Contract approved: {{ contractRecord.title }}', + message: 'Your contract {{ contractRecord.title }} has cleared every approval rung. It can now go out for signature.', sourceObject: 'clm_contract', - sourceId: '{record.id}', + sourceId: ref('record.id'), }, }, @@ -396,7 +397,7 @@ export const ContractApprovalFlow: Flow = { label: 'Mark Rejected', config: { objectName: 'clm_contract', - filter: { id: '{record.id}' }, + filter: { id: must('record.id') }, fields: { status: 'rejected' }, }, }, @@ -405,11 +406,11 @@ export const ContractApprovalFlow: Flow = { type: 'notify', label: 'Notify Owner — Rejected', config: { - recipients: '{contractRecord.owner_id}', + recipients: ref('contractRecord.owner_id'), channels: ['inbox', 'email'], severity: 'warning', topic: 'clm_contract_rejected', - title: 'Contract rejected: {contractRecord.title}', + title: 'Contract rejected: {{ contractRecord.title }}', // §03's way forward is `rejected → draft` 修改重提 — but the copy does // NOT tell the owner to do it themselves, because measured against // card 04's row window they cannot: §04 lets a requester edit their @@ -418,9 +419,9 @@ export const ContractApprovalFlow: Flow = { // contract. Naming the person who can act is the honest instruction; // whether §03 and §04 should agree here is a maintainer decision this // card files rather than settles. - message: 'Your contract {contractRecord.title} was rejected in approval. The approver\'s comment is on the approval record. Ask legal to return the contract to draft if you want to rework and resubmit it.', + message: 'Your contract {{ contractRecord.title }} was rejected in approval. The approver\'s comment is on the approval record. Ask legal to return the contract to draft if you want to rework and resubmit it.', sourceObject: 'clm_contract', - sourceId: '{record.id}', + sourceId: ref('record.id'), }, }, diff --git a/src/flows/contract-intake.flow.ts b/src/flows/contract-intake.flow.ts index fb7f252..019ad8e 100644 --- a/src/flows/contract-intake.flow.ts +++ b/src/flows/contract-intake.flow.ts @@ -1,7 +1,8 @@ -import { P } from '@objectstack/spec'; +import { P, expression } from '@objectstack/spec'; import type { Flow } from '@objectstack/spec/automation'; import { Contract } from '../objects/contract.object.js'; import { Party } from '../objects/party.object.js'; +import { must, ref } from './_values.js'; /** * F1 `contract_intake` — the launch form (DESIGN.md §06 F1, §02 Launch Form, @@ -191,7 +192,7 @@ export const ContractIntakeFlow: Flow = { }, { id: 'get_type', type: 'get_record', label: 'Load Contract Type', - config: { objectName: 'clm_contract_type', filter: { id: '{contract_type}' }, outputVariable: 'typeRecord' }, + config: { objectName: 'clm_contract_type', filter: { id: must('contract_type') }, outputVariable: 'typeRecord' }, }, { id: 'decision_type_ok', type: 'decision', label: 'Type Active?' }, { @@ -215,7 +216,7 @@ export const ContractIntakeFlow: Flow = { { id: 'decision_intake_fields', type: 'decision', label: 'Type Lists Intake Fields?' }, { id: 'intake_from_type', type: 'assignment', label: 'Intake Fields From Type', - config: { assignments: { intakeFields: '{typeRecord.intake_fields}' } }, + config: { assignments: { intakeFields: ref('typeRecord.intake_fields') } }, }, { id: 'intake_none', type: 'assignment', label: 'No Intake Fields', @@ -242,13 +243,13 @@ export const ContractIntakeFlow: Flow = { fields: [ // Carrier: the type's intake_fields, interpolated server-side to the // raw list, hidden by its own predicate, read by the predicates below. - { name: 'intakeFields', label: 'Intake fields', type: 'text', defaultValue: '{intakeFields}', visibleWhen: 'intakeFields == null' }, + { name: 'intakeFields', label: 'Intake fields', type: 'text', defaultValue: ref('intakeFields'), visibleWhen: 'intakeFields == null' }, { name: 'title', label: 'Title', type: 'text', required: true, placeholder: 'e.g. Mutual NDA with Acme' }, - { name: 'our_entity', label: 'Our signing entity', type: 'select', options: optionsOf(Contract, 'our_entity'), defaultValue: '{our_entity}' }, + { name: 'our_entity', label: 'Our signing entity', type: 'select', options: optionsOf(Contract, 'our_entity'), defaultValue: ref('our_entity') }, { name: 'department', label: 'Requesting department', type: 'select', options: optionsOf(Contract, 'department') }, { name: 'amount', label: 'Contract amount', type: 'currency' }, - { name: 'currency_code', label: 'Currency', type: 'select', options: optionsOf(Contract, 'currency_code'), defaultValue: '{currency_code}' }, - { name: 'is_amount_estimated', label: 'Amount is an estimate', type: 'boolean', defaultValue: '{is_amount_estimated}' }, + { name: 'currency_code', label: 'Currency', type: 'select', options: optionsOf(Contract, 'currency_code'), defaultValue: ref('currency_code') }, + { name: 'is_amount_estimated', label: 'Amount is an estimate', type: 'boolean', defaultValue: ref('is_amount_estimated') }, { name: 'start_date', label: 'Start date', type: 'date' }, { name: 'end_date', label: 'End date', type: 'date' }, { name: 'term_months', label: 'Term (months)', type: 'number', placeholder: 'Instead of an end date' }, @@ -257,7 +258,7 @@ export const ContractIntakeFlow: Flow = { { name: 'governing_law', label: 'Governing law', type: 'text', required: true, placeholder: 'e.g. US-NY, England and Wales', visibleWhen: askedFor('governing_law') }, { name: 'payment_terms', label: 'Payment terms', type: 'select', required: true, options: optionsOf(Contract, 'payment_terms'), visibleWhen: askedFor('payment_terms') }, { name: 'confidentiality_term_months', label: 'Confidentiality term (months)', type: 'number', required: true, visibleWhen: askedFor('confidentiality_term_months') }, - { name: 'auto_renew', label: 'Auto-renews', type: 'boolean', defaultValue: '{auto_renew}', visibleWhen: askedFor('auto_renew') }, + { name: 'auto_renew', label: 'Auto-renews', type: 'boolean', defaultValue: ref('auto_renew'), visibleWhen: askedFor('auto_renew') }, // A flat screen has no record picker (see the header): the parent // contract is collected as its record id. { name: 'parent_contract', label: 'Parent contract (record id)', type: 'text', required: true, placeholder: 'The framework or main contract this one sits under', visibleWhen: askedFor('parent_contract') }, @@ -274,7 +275,7 @@ export const ContractIntakeFlow: Flow = { description: 'No counterparty was picked. Create one here — legal keeps its risk flag and screening up to date afterwards.', fields: [ { name: 'new_party_name', label: 'Name', type: 'text', required: true }, - { name: 'new_party_kind', label: 'Kind', type: 'select', options: optionsOf(Party, 'party_kind'), defaultValue: '{new_party_kind}' }, + { name: 'new_party_kind', label: 'Kind', type: 'select', options: optionsOf(Party, 'party_kind'), defaultValue: ref('new_party_kind') }, { name: 'new_party_registration_no', label: 'Registration / tax ID', type: 'text' }, { name: 'new_party_contact_name', label: 'Contact name', type: 'text' }, { name: 'new_party_contact_email', label: 'Contact email', type: 'email' }, @@ -286,19 +287,19 @@ export const ContractIntakeFlow: Flow = { config: { objectName: 'clm_party', fields: { - name: '{new_party_name}', - party_kind: '{new_party_kind}', - registration_no: '{new_party_registration_no}', - contact_name: '{new_party_contact_name}', - contact_email: '{new_party_contact_email}', + name: ref('new_party_name'), + party_kind: ref('new_party_kind'), + registration_no: ref('new_party_registration_no'), + contact_name: ref('new_party_contact_name'), + contact_email: ref('new_party_contact_email'), }, outputVariable: 'createdParty', }, }, - { id: 'use_new_party', type: 'assignment', label: 'Use New Counterparty', config: { assignments: { partyId: '{createdParty.id}' } } }, + { id: 'use_new_party', type: 'assignment', label: 'Use New Counterparty', config: { assignments: { partyId: ref('createdParty.id') } } }, { id: 'get_party', type: 'get_record', label: 'Load Counterparty', - config: { objectName: 'clm_party', filter: { id: '{party}' }, outputVariable: 'partyRecord' }, + config: { objectName: 'clm_party', filter: { id: must('party') }, outputVariable: 'partyRecord' }, }, // The blocked-party refusal, and its two neighbours: a party that does not // resolve is refused too, and a clean one is used. The three edge @@ -306,13 +307,13 @@ export const ContractIntakeFlow: Flow = { { id: 'decision_party_state', type: 'decision', label: 'Counterparty Blocked?' }, { id: 'refuse_party_missing', type: 'script', label: 'Refuse: Counterparty Missing', - config: { function: 'clm_intake_refuse', inputs: { kind: 'party_missing', name: '{party}' } }, + config: { function: 'clm_intake_refuse', inputs: { kind: 'party_missing', name: ref('party') } }, }, { id: 'refuse_blocked', type: 'script', label: 'Refuse: Counterparty Blocked', - config: { function: 'clm_intake_refuse', inputs: { kind: 'party_blocked', name: '{partyRecord.name}', note: '{partyRecord.risk_note}' } }, + config: { function: 'clm_intake_refuse', inputs: { kind: 'party_blocked', name: ref('partyRecord.name'), note: ref('partyRecord.risk_note') } }, }, - { id: 'use_picked_party', type: 'assignment', label: 'Use Picked Counterparty', config: { assignments: { partyId: '{partyRecord.id}' } } }, + { id: 'use_picked_party', type: 'assignment', label: 'Use Picked Counterparty', config: { assignments: { partyId: ref('partyRecord.id') } } }, // ── Step 4: the first version ───────────────────────────────────────── { id: 'decision_document_screen', type: 'decision', label: 'Ask About The Document?' }, @@ -320,9 +321,9 @@ export const ContractIntakeFlow: Flow = { id: 'screen_document', type: 'screen', label: 'First Version', config: { title: 'First version', - description: '{templateNote}', + description: '{{ templateNote }}', fields: [ - { name: 'draft_from_template', label: 'Draft from template', type: 'boolean', defaultValue: '{draft_from_template}' }, + { name: 'draft_from_template', label: 'Draft from template', type: 'boolean', defaultValue: ref('draft_from_template') }, ], }, }, @@ -331,21 +332,21 @@ export const ContractIntakeFlow: Flow = { config: { objectName: 'clm_contract', fields: { - title: '{title}', - contract_type: '{contract_type}', - party: '{partyId}', - our_entity: '{our_entity}', - department: '{department}', - amount: '{amount}', - currency_code: '{currency_code}', - is_amount_estimated: '{is_amount_estimated}', - start_date: '{start_date}', - end_date: '{end_date}', - term_months: '{term_months}', - summary: '{summary}', - governing_law: '{governing_law}', - payment_terms: '{payment_terms}', - confidentiality_term_months: '{confidentiality_term_months}', + title: ref('title'), + contract_type: ref('contract_type'), + party: ref('partyId'), + our_entity: ref('our_entity'), + department: ref('department'), + amount: ref('amount'), + currency_code: ref('currency_code'), + is_amount_estimated: ref('is_amount_estimated'), + start_date: ref('start_date'), + end_date: ref('end_date'), + term_months: ref('term_months'), + summary: ref('summary'), + governing_law: ref('governing_law'), + payment_terms: ref('payment_terms'), + confidentiality_term_months: ref('confidentiality_term_months'), // `liability_cap` is deliberately absent, and the flow does not ask // for it: DESIGN.md §04 makes it read-only for `clm_requester` ("由法 // 务评定"), and the launch form is the requester's. Measured on @@ -354,10 +355,10 @@ export const ContractIntakeFlow: Flow = { // to edit [liability_cap]" — on CREATE and on UPDATE alike, so // moving the write to a later node does not rescue it. Legal sets // the cap during review, where §04 puts it. - auto_renew: '{auto_renew}', - parent_contract: '{parent_contract}', + auto_renew: ref('auto_renew'), + parent_contract: ref('parent_contract'), status: 'draft', - owner_id: '{$User.Id}', + owner_id: expression('current_user.id'), }, outputVariable: 'contractRecord', }, @@ -368,7 +369,7 @@ export const ContractIntakeFlow: Flow = { config: { objectName: 'clm_contract_version', fields: { - contract: '{contractRecord.id}', + contract: ref('contractRecord.id'), version_no: 1, kind: 'draft', turn: 'internal', @@ -376,8 +377,8 @@ export const ContractIntakeFlow: Flow = { // A file field READS as `{ id, name, size, mimeType, url }` and is // WRITTEN as the id (measured: the object form of the value is // refused with "expected string, received object"). - file: '{typeRecord.template_file.id}', - submitted_by: '{$User.Id}', + file: ref('typeRecord.template_file.id'), + submitted_by: expression('current_user.id'), notes: 'Drafted from the contract type template.', }, outputVariable: 'versionRecord', @@ -388,13 +389,13 @@ export const ContractIntakeFlow: Flow = { config: { objectName: 'clm_contract_version', fields: { - contract: '{contractRecord.id}', + contract: ref('contractRecord.id'), version_no: 1, kind: 'draft', turn: 'internal', is_current: true, - file: '{first_version_file}', - submitted_by: '{$User.Id}', + file: ref('first_version_file'), + submitted_by: expression('current_user.id'), }, outputVariable: 'versionRecord', }, @@ -407,7 +408,7 @@ export const ContractIntakeFlow: Flow = { description: 'The document this contract starts from. It becomes version 1.', objectName: 'clm_contract_version', mode: 'create', - defaults: { contract: '{contractRecord.id}', version_no: 1, kind: 'draft', turn: 'internal', is_current: true }, + defaults: { contract: ref('contractRecord.id'), version_no: 1, kind: 'draft', turn: 'internal', is_current: true }, idVariable: 'versionId', }, }, @@ -420,7 +421,7 @@ export const ContractIntakeFlow: Flow = { title: 'Submit the contract', description: 'Choose whether to submit now. Submitting routes the contract to legal review or to approval, as its type decides.', fields: [ - { name: 'submit_now', label: 'Submit now', type: 'boolean', defaultValue: '{submit_now}' }, + { name: 'submit_now', label: 'Submit now', type: 'boolean', defaultValue: ref('submit_now') }, ], }, }, @@ -433,7 +434,7 @@ export const ContractIntakeFlow: Flow = { // taken by `contract_route_onward` as a system write (`contract.hook.ts` // says why it is a second write; issue #90 is what the single write cost). id: 'submit_contract', type: 'update_record', label: 'Submit Contract', - config: { objectName: 'clm_contract', filter: { id: '{contractRecord.id}' }, fields: { status: 'submitted' } }, + config: { objectName: 'clm_contract', filter: { id: must('contractRecord.id') }, fields: { status: 'submitted' } }, }, { id: 'end', type: 'end', label: 'End' }, ], diff --git a/src/flows/executed-upload.flow.ts b/src/flows/executed-upload.flow.ts index d5cc529..d143033 100644 --- a/src/flows/executed-upload.flow.ts +++ b/src/flows/executed-upload.flow.ts @@ -1,4 +1,5 @@ import type { Flow } from '@objectstack/spec/automation'; +import { must, ref } from './_values.js'; /** * F16 `executed_upload` — backfilling a contract that was signed before this @@ -167,7 +168,7 @@ export const ExecutedUploadFlow: Flow = { id: 'check_date', type: 'script', label: 'Check The Signing Date', - config: { function: 'clm_backfill_stamps', inputs: { signedDate: '{signed_date}' }, outputVariable: 'stamps' }, + config: { function: 'clm_backfill_stamps', inputs: { signedDate: ref('signed_date') }, outputVariable: 'stamps' }, }, { id: 'decide_date', type: 'decision', label: 'Signing Date Usable?' }, { @@ -176,7 +177,7 @@ export const ExecutedUploadFlow: Flow = { label: 'Refuse — Signing Date', // The message `check_date` computed, handed back to the caller. See // `refuseBackfill` for why this is a throwing script and not an `end`. - config: { function: 'clm_backfill_refuse', inputs: { message: '{stamps.problem}', code: 'VALIDATION_FAILED' } }, + config: { function: 'clm_backfill_refuse', inputs: { message: ref('stamps.problem'), code: 'VALIDATION_FAILED' } }, }, { @@ -185,7 +186,7 @@ export const ExecutedUploadFlow: Flow = { label: 'Load Counterparty', config: { objectName: 'clm_party', - filter: { id: '{party}' }, + filter: { id: must('party') }, fields: ['id', 'name', 'risk_flag'], outputVariable: 'partyRecord', }, @@ -214,21 +215,21 @@ export const ExecutedUploadFlow: Flow = { config: { objectName: 'clm_contract', fields: { - title: '{title}', - contract_type: '{contract_type}', - party: '{party}', - our_entity: '{our_entity}', - department: '{department}', - amount: '{amount}', - currency_code: '{currency_code}', - start_date: '{start_date}', - end_date: '{end_date}', - term_months: '{term_months}', - auto_renew: '{auto_renew}', - renewal_notice_days: '{renewal_notice_days}', - governing_law: '{governing_law}', - summary: '{summary}', - archive_no: '{archive_no}', + title: ref('title'), + contract_type: ref('contract_type'), + party: ref('party'), + our_entity: ref('our_entity'), + department: ref('department'), + amount: ref('amount'), + currency_code: ref('currency_code'), + start_date: ref('start_date'), + end_date: ref('end_date'), + term_months: ref('term_months'), + auto_renew: ref('auto_renew'), + renewal_notice_days: ref('renewal_notice_days'), + governing_law: ref('governing_law'), + summary: ref('summary'), + archive_no: ref('archive_no'), // The two facts a backfill IS. status: 'active', is_backfilled: true, @@ -245,9 +246,9 @@ export const ExecutedUploadFlow: Flow = { // // It is the SIGNING date, not now: a backfilled contract came into // force when it was executed, not when somebody typed it in. - signed_at: '{stamps.signedAt}', - executed_at: '{stamps.signedAt}', - activated_at: '{stamps.signedAt}', + signed_at: ref('stamps.signedAt'), + executed_at: ref('stamps.signedAt'), + activated_at: ref('stamps.signedAt'), }, outputVariable: 'contractRecord', }, @@ -259,14 +260,14 @@ export const ExecutedUploadFlow: Flow = { config: { objectName: 'clm_contract_version', fields: { - contract: '{contractRecord.id}', + contract: ref('contractRecord.id'), version_no: 1, kind: 'final_signed', turn: 'internal', // WRITTEN as the id: a file field READS as `{ id, name, size, … }` // and card 05 measured the object form refused with "expected // string, received object". - file: '{executed_file}', + file: ref('executed_file'), is_current: true, notes: 'Executed copy supplied with the backfill.', }, diff --git a/src/flows/expiration-sweep.flow.ts b/src/flows/expiration-sweep.flow.ts index 58b27e3..9a4ca1e 100644 --- a/src/flows/expiration-sweep.flow.ts +++ b/src/flows/expiration-sweep.flow.ts @@ -1,6 +1,7 @@ import type { Flow } from '@objectstack/spec/automation'; import { HAS_RECIPIENT, NO_RECIPIENT, rowBody, sweepFlow } from './_daily-sweep.js'; import { RENEWAL_DRAFT_FIELDS } from './renewal-notice.flow.js'; +import { must, ref } from './_values.js'; /** * F13 `expiration_sweep` — the day a contract's term runs out (DESIGN.md §06 @@ -71,19 +72,19 @@ export const ExpirationSweepFlow: Flow = sweepFlow({ label: 'Mark Expired', config: { objectName: 'clm_contract', - filter: { id: '{expired.id}' }, + filter: { id: must('expired.id') }, fields: { status: 'expired' }, }, }, ], - recipients: { primary: '{expired.owner_id}', also: '{expired.legal_owner}' }, + recipients: { primary: ref('expired.owner_id'), also: ref('expired.legal_owner') }, notice: { topic: 'clm_contract_expired', severity: 'warning', sourceObject: 'clm_contract', - sourceId: '{expired.id}', - title: 'Expired: {expired.title}', - message: 'Contract {expired.contract_number} ended on {expired.end_date} and is now expired. It does not auto-renew. Start a renewal if the relationship continues, and hand it to the records desk for archiving.', + sourceId: ref('expired.id'), + title: 'Expired: {{ expired.title }}', + message: 'Contract {{ expired.contract_number }} ended on {{ expired.end_date }} and is now expired. It does not auto-renew. Start a renewal if the relationship continues, and hand it to the records desk for archiving.', }, }), }, @@ -106,7 +107,7 @@ export const ExpirationSweepFlow: Flow = sweepFlow({ label: 'Renewal Already Drafted?', config: { objectName: 'clm_contract', - filter: { renewed_from: '{expired.id}' }, + filter: { renewed_from: must('expired.id') }, fields: ['id', 'contract_number', 'status'], outputVariable: 'existingRenewal', }, @@ -118,7 +119,7 @@ export const ExpirationSweepFlow: Flow = sweepFlow({ label: 'Compute The Draft', config: { function: 'clm_renewal_draft', - inputs: { title: '{expired.title}', endDate: '{expired.end_date}', termMonths: '{expired.term_months}' }, + inputs: { title: ref('expired.title'), endDate: ref('expired.end_date'), termMonths: ref('expired.term_months') }, outputVariable: 'draft', }, }, @@ -138,7 +139,7 @@ export const ExpirationSweepFlow: Flow = sweepFlow({ label: 'Who To Tell', config: { function: 'clm_notify_recipients', - inputs: { primary: '{expired.owner_id}', also: '{expired.legal_owner}' }, + inputs: { primary: ref('expired.owner_id'), also: ref('expired.legal_owner') }, outputVariable: 'recipients', }, }, @@ -148,14 +149,14 @@ export const ExpirationSweepFlow: Flow = sweepFlow({ type: 'notify', label: 'Notify', config: { - recipients: '{recipients.userIds}', + recipients: ref('recipients.userIds'), channels: ['inbox', 'email'], severity: 'warning', topic: 'clm_contract_auto_renewed', sourceObject: 'clm_contract', - sourceId: '{renewal.id}', - title: 'Renewal drafted: {expired.title}', - message: 'Contract {expired.contract_number} auto-renews and its term ended on {expired.end_date}, so a renewal draft has been created and linked back to it. Review the terms and submit it.', + sourceId: ref('renewal.id'), + title: 'Renewal drafted: {{ expired.title }}', + message: 'Contract {{ expired.contract_number }} auto-renews and its term ended on {{ expired.end_date }}, so a renewal draft has been created and linked back to it. Review the terms and submit it.', }, }, { id: 'auto_done', type: 'assignment', label: 'Row Done' }, diff --git a/src/flows/legal-review-sla.flow.ts b/src/flows/legal-review-sla.flow.ts index 7e1f0f8..ada78d8 100644 --- a/src/flows/legal-review-sla.flow.ts +++ b/src/flows/legal-review-sla.flow.ts @@ -1,5 +1,6 @@ import type { Flow } from '@objectstack/spec/automation'; import { holdersPrelude, rowBody, sweepFlow } from './_daily-sweep.js'; +import { ref } from './_values.js'; /** * F3 `legal_review_sla` — the daily nudge on contracts sitting in legal review @@ -70,14 +71,14 @@ export const LegalReviewSlaFlow: Flow = sweepFlow({ item: 'review', body: rowBody({ id: 'over30', - recipients: { primary: '{review.legal_owner}' }, + recipients: { primary: ref('review.legal_owner') }, notice: { topic: 'clm_legal_review_over_30_days', severity: 'warning', sourceObject: 'clm_contract', - sourceId: '{review.id}', - title: 'In legal review over 30 days: {review.title}', - message: 'Contract {review.contract_number} has been in legal review since {review.review_started_at} — more than 30 days. This is a fixed 30-day threshold, not this contract type\'s own review SLA. Move it on, or record what it is waiting for.', + sourceId: ref('review.id'), + title: 'In legal review over 30 days: {{ review.title }}', + message: 'Contract {{ review.contract_number }} has been in legal review since {{ review.review_started_at }} — more than 30 days. This is a fixed 30-day threshold, not this contract type\'s own review SLA. Move it on, or record what it is waiting for.', }, }), }, @@ -93,14 +94,14 @@ export const LegalReviewSlaFlow: Flow = sweepFlow({ item: 'review', body: rowBody({ id: 'over60', - recipients: { primary: '{review.legal_owner}', also: '{legalHeads.userIds}' }, + recipients: { primary: ref('review.legal_owner'), also: ref('legalHeads.userIds') }, notice: { topic: 'clm_legal_review_over_60_days', severity: 'critical', sourceObject: 'clm_contract', - sourceId: '{review.id}', - title: 'In legal review over 60 days: {review.title}', - message: 'Contract {review.contract_number} has been in legal review since {review.review_started_at} — more than 60 days, twice the 30-day reminder threshold, so the head of legal is copied. This is a fixed threshold, not this contract type\'s own review SLA.', + sourceId: ref('review.id'), + title: 'In legal review over 60 days: {{ review.title }}', + message: 'Contract {{ review.contract_number }} has been in legal review since {{ review.review_started_at }} — more than 60 days, twice the 30-day reminder threshold, so the head of legal is copied. This is a fixed threshold, not this contract type\'s own review SLA.', }, }), }, diff --git a/src/flows/obligation-due.flow.ts b/src/flows/obligation-due.flow.ts index b65f709..898235b 100644 --- a/src/flows/obligation-due.flow.ts +++ b/src/flows/obligation-due.flow.ts @@ -1,5 +1,6 @@ import type { Flow, FlowNode } from '@objectstack/spec/automation'; import { rowBody, sweepFlow } from './_daily-sweep.js'; +import { must, ref } from './_values.js'; /** * F10 `obligation_due` — the obligation reminder and the arrears sweep @@ -47,7 +48,7 @@ const flagOverdue: FlowNode = { label: 'Mark Overdue', config: { objectName: 'clm_obligation', - filter: { id: '{obligation.id}' }, + filter: { id: must('obligation.id') }, // The ONE writer of this value. `obligation_state_machine` refuses // `overdue` from any non-system write on both the insert and the update // path, so this job is the only thing in the product that can set it — @@ -73,14 +74,14 @@ export const ObligationDueFlow: Flow = sweepFlow({ item: 'obligation', body: rowBody({ id: 'week', - recipients: { primary: '{obligation.owner}' }, + recipients: { primary: ref('obligation.owner') }, notice: { topic: 'clm_obligation_due_soon', severity: 'info', sourceObject: 'clm_obligation', - sourceId: '{obligation.id}', - title: 'Due in 7 days: {obligation.title}', - message: 'This contract obligation is due on {obligation.due_date}. Record progress, or mark it done once it is performed.', + sourceId: ref('obligation.id'), + title: 'Due in 7 days: {{ obligation.title }}', + message: 'This contract obligation is due on {{ obligation.due_date }}. Record progress, or mark it done once it is performed.', }, }), }, @@ -96,14 +97,14 @@ export const ObligationDueFlow: Flow = sweepFlow({ item: 'obligation', body: rowBody({ id: 'today', - recipients: { primary: '{obligation.owner}' }, + recipients: { primary: ref('obligation.owner') }, notice: { topic: 'clm_obligation_due_today', severity: 'warning', sourceObject: 'clm_obligation', - sourceId: '{obligation.id}', - title: 'Due today: {obligation.title}', - message: 'This contract obligation is due today ({obligation.due_date}). It moves into overdue tomorrow if it is not done or waived.', + sourceId: ref('obligation.id'), + title: 'Due today: {{ obligation.title }}', + message: 'This contract obligation is due today ({{ obligation.due_date }}). It moves into overdue tomorrow if it is not done or waived.', }, }), }, @@ -122,14 +123,14 @@ export const ObligationDueFlow: Flow = sweepFlow({ body: rowBody({ id: 'arrears', lead: [flagOverdue], - recipients: { primary: '{obligation.owner}' }, + recipients: { primary: ref('obligation.owner') }, notice: { topic: 'clm_obligation_overdue', severity: 'critical', sourceObject: 'clm_obligation', - sourceId: '{obligation.id}', - title: 'Overdue: {obligation.title}', - message: 'This contract obligation was due on {obligation.due_date} and is now overdue. Perform it and mark it done, or ask legal to waive it.', + sourceId: ref('obligation.id'), + title: 'Overdue: {{ obligation.title }}', + message: 'This contract obligation was due on {{ obligation.due_date }} and is now overdue. Perform it and mark it done, or ask legal to waive it.', }, }), }, diff --git a/src/flows/payment-overdue.flow.ts b/src/flows/payment-overdue.flow.ts index e3c519f..d6110f1 100644 --- a/src/flows/payment-overdue.flow.ts +++ b/src/flows/payment-overdue.flow.ts @@ -1,5 +1,6 @@ import type { Flow, FlowNode } from '@objectstack/spec/automation'; import { holdersPrelude, rowBody, sweepFlow } from './_daily-sweep.js'; +import { must, ref } from './_values.js'; /** * F11 `payment_overdue` — instalments that have come due and instalments that @@ -47,7 +48,7 @@ const markDue: FlowNode = { label: 'Mark Due', config: { objectName: 'clm_payment_plan', - filter: { id: '{instalment.id}' }, + filter: { id: must('instalment.id') }, fields: { status: 'due' }, }, }; @@ -59,7 +60,7 @@ const markOverdue: FlowNode = { label: 'Mark Overdue', config: { objectName: 'clm_payment_plan', - filter: { id: '{instalment.id}' }, + filter: { id: must('instalment.id') }, fields: { status: 'overdue' }, }, }; @@ -71,7 +72,7 @@ const loadContract: FlowNode = { label: 'Load Contract', config: { objectName: 'clm_contract', - filter: { id: '{instalment.contract}' }, + filter: { id: must('instalment.contract') }, fields: ['id', 'title', 'contract_number', 'owner_id', 'currency_code'], outputVariable: 'parentContract', }, @@ -112,14 +113,14 @@ export const PaymentOverdueFlow: Flow = sweepFlow({ body: rowBody({ id: 'arrears', lead: [markOverdue, loadContract], - recipients: { primary: '{parentContract.owner_id}', also: '{financeControllers.userIds}' }, + recipients: { primary: ref('parentContract.owner_id'), also: ref('financeControllers.userIds') }, notice: { topic: 'clm_payment_overdue', severity: 'critical', sourceObject: 'clm_payment_plan', - sourceId: '{instalment.id}', - title: 'Instalment {instalment.seq} overdue: {parentContract.title}', - message: 'Instalment {instalment.seq} of contract {parentContract.contract_number} was due on {instalment.planned_date} and is not settled. Planned {instalment.planned_amount}; received so far {instalment.actual_amount}.', + sourceId: ref('instalment.id'), + title: 'Instalment {{ instalment.seq }} overdue: {{ parentContract.title }}', + message: 'Instalment {{ instalment.seq }} of contract {{ parentContract.contract_number }} was due on {{ instalment.planned_date }} and is not settled. Planned {{ instalment.planned_amount }}; received so far {{ instalment.actual_amount }}.', }, }), }, diff --git a/src/flows/renewal-notice.flow.ts b/src/flows/renewal-notice.flow.ts index 27777ef..787a6e6 100644 --- a/src/flows/renewal-notice.flow.ts +++ b/src/flows/renewal-notice.flow.ts @@ -1,5 +1,7 @@ +import { expression } from '@objectstack/spec'; import type { Flow, FlowNode } from '@objectstack/spec/automation'; import { HAS_RECIPIENT, NO_RECIPIENT, SWEEP_LIMIT, sweepFlow } from './_daily-sweep.js'; +import { must, ref } from './_values.js'; /** * F12 `renewal_notice` — the renewal window, and the action that acts on it @@ -118,7 +120,7 @@ const computeWindow: FlowNode = { label: 'Inside The Notice Window?', config: { function: 'clm_renewal_window', - inputs: { endDate: '{expiring.end_date}', noticeDays: '{expiring.renewal_notice_days}' }, + inputs: { endDate: ref('expiring.end_date'), noticeDays: ref('expiring.renewal_notice_days') }, outputVariable: 'window', }, }; @@ -162,7 +164,7 @@ export const RenewalNoticeFlow: Flow = sweepFlow({ label: 'Flag Expiring', config: { objectName: 'clm_contract', - filter: { id: '{expiring.id}' }, + filter: { id: must('expiring.id') }, // §12 gap #7's declared v1 degradation, and the only thing this // job stamps: a boolean §03 already carries. NOT a duration. fields: { is_expiring: true }, @@ -174,7 +176,7 @@ export const RenewalNoticeFlow: Flow = sweepFlow({ label: 'Who To Tell', config: { function: 'clm_notify_recipients', - inputs: { primary: '{expiring.owner_id}', also: '{expiring.legal_owner}' }, + inputs: { primary: ref('expiring.owner_id'), also: ref('expiring.legal_owner') }, outputVariable: 'recipients', }, }, @@ -184,14 +186,14 @@ export const RenewalNoticeFlow: Flow = sweepFlow({ type: 'notify', label: 'Notify', config: { - recipients: '{recipients.userIds}', + recipients: ref('recipients.userIds'), channels: ['inbox', 'email'], severity: 'warning', topic: 'clm_renewal_notice_due', sourceObject: 'clm_contract', - sourceId: '{expiring.id}', - title: 'Renewal decision due: {expiring.title}', - message: 'Contract {expiring.contract_number} ends on {expiring.end_date}, and notice of non-renewal is owed by {window.deadline} ({window.noticeDays} days before). Start a renewal from the contract page, or give notice.', + sourceId: ref('expiring.id'), + title: 'Renewal decision due: {{ expiring.title }}', + message: 'Contract {{ expiring.contract_number }} ends on {{ expiring.end_date }}, and notice of non-renewal is owed by {{ window.deadline }} ({{ window.noticeDays }} days before). Start a renewal from the contract page, or give notice.', }, }, { id: 'notice_done', type: 'assignment', label: 'Row Done' }, @@ -243,7 +245,7 @@ export const RenewalStartFlow: Flow = { label: 'Load Source Contract', config: { objectName: 'clm_contract', - filter: { id: '{recordId}' }, + filter: { id: must('recordId') }, fields: ['id', 'title', 'contract_type', 'party', 'our_entity', 'department', 'owner_id', 'legal_owner', 'amount', 'currency_code', 'payment_terms', 'governing_law', 'jurisdiction', 'contract_language', 'confidentiality_term_months', 'auto_renew', 'renewal_notice_days', 'term_months', 'end_date', 'parent_contract'], outputVariable: 'source', }, @@ -254,7 +256,7 @@ export const RenewalStartFlow: Flow = { label: 'Renewal Already Started?', config: { objectName: 'clm_contract', - filter: { renewed_from: '{recordId}' }, + filter: { renewed_from: must('recordId') }, fields: ['id', 'contract_number', 'status'], outputVariable: 'existingRenewal', }, @@ -278,7 +280,11 @@ export const RenewalStartFlow: Flow = { // `contract_intake` already uses, so that is what both flows use. function: 'clm_backfill_refuse', inputs: { - message: 'This contract already has a renewal draft ({existingRenewal.contract_number}). Open that one instead of starting a second.', + // A value slot, so the sentence is a CEL string: no `{{ }}` hole + // here. This branch runs only once `existingRenewal` matched a row. + message: expression( + '"This contract already has a renewal draft (" + string(existingRenewal.contract_number) + "). Open that one instead of starting a second."', + ), code: 'INVALID_STATE', }, }, @@ -289,7 +295,7 @@ export const RenewalStartFlow: Flow = { label: 'Compute The Draft', config: { function: 'clm_renewal_draft', - inputs: { title: '{source.title}', endDate: '{source.end_date}', termMonths: '{source.term_months}' }, + inputs: { title: ref('source.title'), endDate: ref('source.end_date'), termMonths: ref('source.term_months') }, outputVariable: 'draft', }, }, @@ -300,26 +306,26 @@ export const RenewalStartFlow: Flow = { config: { objectName: 'clm_contract', fields: { - title: '{draft.title}', - contract_type: '{source.contract_type}', - party: '{source.party}', - our_entity: '{source.our_entity}', - department: '{source.department}', - amount: '{source.amount}', - currency_code: '{source.currency_code}', - payment_terms: '{source.payment_terms}', - governing_law: '{source.governing_law}', - jurisdiction: '{source.jurisdiction}', - contract_language: '{source.contract_language}', - confidentiality_term_months: '{source.confidentiality_term_months}', - auto_renew: '{source.auto_renew}', - renewal_notice_days: '{source.renewal_notice_days}', - term_months: '{draft.termMonths}', - start_date: '{draft.startDate}', - end_date: '{draft.endDate}', - parent_contract: '{source.parent_contract}', + title: ref('draft.title'), + contract_type: ref('source.contract_type'), + party: ref('source.party'), + our_entity: ref('source.our_entity'), + department: ref('source.department'), + amount: ref('source.amount'), + currency_code: ref('source.currency_code'), + payment_terms: ref('source.payment_terms'), + governing_law: ref('source.governing_law'), + jurisdiction: ref('source.jurisdiction'), + contract_language: ref('source.contract_language'), + confidentiality_term_months: ref('source.confidentiality_term_months'), + auto_renew: ref('source.auto_renew'), + renewal_notice_days: ref('source.renewal_notice_days'), + term_months: ref('draft.termMonths'), + start_date: ref('draft.startDate'), + end_date: ref('draft.endDate'), + parent_contract: ref('source.parent_contract'), // The link that makes this a renewal rather than a copy (§03). - renewed_from: '{source.id}', + renewed_from: ref('source.id'), // `status` is left to its `draft` default on purpose: the type stamp // hook refuses a contract born in any other state, and a renewal // earns its way through the machine like any other contract. @@ -351,31 +357,31 @@ export const RenewalStartFlow: Flow = { /** The auto-renew draft F13 creates without a button — same field map, one caller over. */ export const RENEWAL_DRAFT_FIELDS = { - title: '{draft.title}', - contract_type: '{expired.contract_type}', - party: '{expired.party}', - our_entity: '{expired.our_entity}', - department: '{expired.department}', - amount: '{expired.amount}', - currency_code: '{expired.currency_code}', - payment_terms: '{expired.payment_terms}', - governing_law: '{expired.governing_law}', - jurisdiction: '{expired.jurisdiction}', - contract_language: '{expired.contract_language}', - confidentiality_term_months: '{expired.confidentiality_term_months}', - auto_renew: '{expired.auto_renew}', - renewal_notice_days: '{expired.renewal_notice_days}', - term_months: '{draft.termMonths}', - start_date: '{draft.startDate}', - end_date: '{draft.endDate}', - parent_contract: '{expired.parent_contract}', - renewed_from: '{expired.id}', + title: ref('draft.title'), + contract_type: ref('expired.contract_type'), + party: ref('expired.party'), + our_entity: ref('expired.our_entity'), + department: ref('expired.department'), + amount: ref('expired.amount'), + currency_code: ref('expired.currency_code'), + payment_terms: ref('expired.payment_terms'), + governing_law: ref('expired.governing_law'), + jurisdiction: ref('expired.jurisdiction'), + contract_language: ref('expired.contract_language'), + confidentiality_term_months: ref('expired.confidentiality_term_months'), + auto_renew: ref('expired.auto_renew'), + renewal_notice_days: ref('expired.renewal_notice_days'), + term_months: ref('draft.termMonths'), + start_date: ref('draft.startDate'), + end_date: ref('draft.endDate'), + parent_contract: ref('expired.parent_contract'), + renewed_from: ref('expired.id'), // The sweep runs `runAs: 'system'`, and a system insert is exactly the case // the security middleware stands aside for ("if (opCtx.context?.isSystem) // return next()"), so `owner_id` would be left EMPTY unless it is carried // over. An unowned renewal draft is one nobody's 我的合同 list shows. - owner_id: '{expired.owner_id}', - legal_owner: '{expired.legal_owner}', + owner_id: ref('expired.owner_id'), + legal_owner: ref('expired.legal_owner'), } as const; /** Rows read per stage, re-exported so F13's cap and F12's stay one number. */ diff --git a/src/flows/signature-record.flow.ts b/src/flows/signature-record.flow.ts index a246abd..387a21e 100644 --- a/src/flows/signature-record.flow.ts +++ b/src/flows/signature-record.flow.ts @@ -1,6 +1,7 @@ import { P, expression } from '@objectstack/spec'; import type { Flow } from '@objectstack/spec/automation'; import { Signature } from '../objects/signature.object.js'; +import { must, ref } from './_values.js'; /** * F7 `signature_record` — the execution-formalities check (DESIGN.md §06 F7, @@ -238,13 +239,13 @@ export const SignatureRecordFlow: Flow = { // Re-read rather than trusting the trigger snapshot: `formalities_done` // is the value being compared, and the flow may also be entered from the // insert leg where the snapshot is the input payload. - config: { objectName: 'clm_signature', filter: { id: '{record.id}' }, outputVariable: 'signatureRecord' }, + config: { objectName: 'clm_signature', filter: { id: must('record.id') }, outputVariable: 'signatureRecord' }, }, { id: 'get_contract', type: 'get_record', label: 'Get Contract', - config: { objectName: 'clm_contract', filter: { id: '{signatureRecord.contract}' }, outputVariable: 'contractRecord' }, + config: { objectName: 'clm_contract', filter: { id: must('signatureRecord.contract') }, outputVariable: 'contractRecord' }, }, { id: 'compare_formalities', @@ -253,11 +254,11 @@ export const SignatureRecordFlow: Flow = { config: { function: 'clm_signature_execution', inputs: { - required: '{contractRecord.execution_formalities}', - done: '{signatureRecord.formalities_done}', - versionCount: '{contractRecord.version_count}', - legalOwner: '{contractRecord.legal_owner}', - contractOwner: '{contractRecord.owner_id}', + required: ref('contractRecord.execution_formalities'), + done: ref('signatureRecord.formalities_done'), + versionCount: ref('contractRecord.version_count'), + legalOwner: ref('contractRecord.legal_owner'), + contractOwner: ref('contractRecord.owner_id'), }, outputVariable: 'execution', }, @@ -271,13 +272,13 @@ export const SignatureRecordFlow: Flow = { label: 'Stamp Executed At', config: { objectName: 'clm_contract', - filter: { id: '{contractRecord.id}' }, + filter: { id: must('contractRecord.id') }, // `signed_at` is NOT written here. DESIGN.md §03 gives it to the // `signing → active` guard (which stamps both from the completed // round) and to F8's e-signature callback; F7's own sentence is // "盖合同 executed_at". Two writers for one column is the thing the // routing note in contract.hook.ts warns about. - fields: { executed_at: '{signatureRecord.completed_at}' }, + fields: { executed_at: ref('signatureRecord.completed_at') }, }, }, { @@ -289,7 +290,7 @@ export const SignatureRecordFlow: Flow = { // guarded rather than only `has()`-guarded. config: { objectName: 'clm_contract_version', - filter: { contract: '{contractRecord.id}', kind: 'final_signed' }, + filter: { contract: must('contractRecord.id'), kind: 'final_signed' }, fields: ['id', 'version_no'], outputVariable: 'existingFinal', }, @@ -302,8 +303,8 @@ export const SignatureRecordFlow: Flow = { config: { objectName: 'clm_contract_version', fields: { - contract: '{contractRecord.id}', - version_no: '{execution.nextVersionNo}', + contract: ref('contractRecord.id'), + version_no: ref('execution.nextVersionNo'), kind: 'final_signed', turn: 'internal', // `is_current` is deliberately left at its `false` default. The @@ -316,7 +317,7 @@ export const SignatureRecordFlow: Flow = { // A file field READS as `{ id, name, size, mimeType, url }` and is // WRITTEN as the id (card 05 measured the object form refused with // "expected string, received object"). - file: '{signatureRecord.executed_file.id}', + file: ref('signatureRecord.executed_file.id'), notes: 'Executed copy filed from the completed signature round.', }, outputVariable: 'finalVersion', @@ -327,14 +328,14 @@ export const SignatureRecordFlow: Flow = { type: 'notify', label: 'Notify — Executed Copy Missing', config: { - recipients: '{execution.recipient}', + recipients: ref('execution.recipient'), channels: ['inbox', 'email'], severity: 'warning', topic: 'clm_execution_copy_missing', - title: 'Executed copy missing: {contractRecord.title}', + title: 'Executed copy missing: {{ contractRecord.title }}', message: 'Every execution formality is done on the completed signing round, but no executed copy is attached, so no final signed version could be filed. Upload the executed document on the signature record.', sourceObject: 'clm_signature', - sourceId: '{signatureRecord.id}', + sourceId: ref('signatureRecord.id'), }, }, @@ -345,17 +346,17 @@ export const SignatureRecordFlow: Flow = { type: 'notify', label: 'Notify — Formalities Missing', config: { - recipients: '{execution.recipient}', + recipients: ref('execution.recipient'), channels: ['inbox', 'email'], severity: 'warning', topic: 'clm_execution_formalities_missing', - title: 'Execution formalities outstanding: {contractRecord.title}', + title: 'Execution formalities outstanding: {{ contractRecord.title }}', // The whole point of F7's short branch is naming WHICH one — a // notification that says "something is missing" sends the reader back // to the same comparison the flow just did. - message: 'The signing round completed, but this contract cannot be activated until these execution formalities are recorded on the signature round: {execution.missingText}.', + message: 'The signing round completed, but this contract cannot be activated until these execution formalities are recorded on the signature round: {{ execution.missingText }}.', sourceObject: 'clm_signature', - sourceId: '{signatureRecord.id}', + sourceId: ref('signatureRecord.id'), }, }, diff --git a/src/flows/turn-stalled.flow.ts b/src/flows/turn-stalled.flow.ts index d096086..6775c0f 100644 --- a/src/flows/turn-stalled.flow.ts +++ b/src/flows/turn-stalled.flow.ts @@ -1,5 +1,6 @@ import type { Flow } from '@objectstack/spec/automation'; import { rowBody, sweepFlow } from './_daily-sweep.js'; +import { ref } from './_values.js'; /** * F4 `turn_stalled` — the counterparty has had the ball for over a week @@ -52,14 +53,14 @@ export const TurnStalledFlow: Flow = sweepFlow({ item: 'stalled', body: rowBody({ id: 'stalled', - recipients: { primary: '{stalled.owner_id}' }, + recipients: { primary: ref('stalled.owner_id') }, notice: { topic: 'clm_turn_stalled', severity: 'warning', sourceObject: 'clm_contract', - sourceId: '{stalled.id}', - title: 'Waiting on the counterparty since {stalled.turn_since}: {stalled.title}', - message: 'The turn on contract {stalled.contract_number} has been with the counterparty for more than 7 days. Chase them, or take the turn back and record where the negotiation stands.', + sourceId: ref('stalled.id'), + title: 'Waiting on the counterparty since {{ stalled.turn_since }}: {{ stalled.title }}', + message: 'The turn on contract {{ stalled.contract_number }} has been with the counterparty for more than 7 days. Chase them, or take the turn back and record where the negotiation stands.', }, }), }, diff --git a/src/pages/contract_detail.page.ts b/src/pages/contract_detail.page.ts index 6f0d79b..99b93d9 100644 --- a/src/pages/contract_detail.page.ts +++ b/src/pages/contract_detail.page.ts @@ -93,7 +93,6 @@ export const ContractDetailPage: Page = { type: 'record', object: 'clm_contract', kind: 'slotted', - template: 'full-width', // The object's default record page for every audience. No // `assignedProfiles`: §05 gives the five audiences ONE detail page and varies diff --git a/src/profiles/requester.profile.ts b/src/profiles/requester.profile.ts index 36a2df3..b49df7a 100644 --- a/src/profiles/requester.profile.ts +++ b/src/profiles/requester.profile.ts @@ -21,7 +21,7 @@ import { CONTRACT_STAMPED_FIELDS, CONTRACT_TERMINATION_REASON, hidden, inList, r * the default is DISTRIBUTED, not implicit: * * - every one of the seven positions is bound to this set as well as to its - * own (`src/security/bind-position-sets.ts`), so legal, finance, records, + * own (`permissionSets` in `src/sharing/positions.ts`), so legal, finance, records, * admin and the two leadership rungs are all requesters too — which is also * the only way an executive holds an object-level read on `clm_contract` * for the rows `contract_executive_routed` shares with them; diff --git a/src/security/bind-position-sets.ts b/src/security/bind-position-sets.ts deleted file mode 100644 index 8685842..0000000 --- a/src/security/bind-position-sets.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { AdminSet, FinanceSet, LegalSet, RecordsSet, RequesterSet } from '../profiles/index.js'; -import { CLM_POSITION } from '../sharing/positions.js'; - -/** - * Position ↔ permission-set bindings (DESIGN.md §04). - * - * The permission model is record-authoritative (ADR-0090/0094): declaring a - * position and declaring a set grants nobody anything until a - * `sys_position_permission_set` row joins them. Without this file every - * persona silently degrades to the platform baseline — which reads as - * "permissions are broken" rather than "a binding is missing". - * - * This cannot be a declarative seed: the seed loader runs before the security - * bootstrap creates the `sys_position` / `sys_permission_set` rows, so the - * name references would not resolve. We play the admin's part imperatively on - * `kernel:bootstrapped` — the anchor that fires only after every - * `kernel:ready` handler, the security bootstrap included, has settled (the - * shape ATS ships in its `bind-position-sets.ts`). - * - * Two rows per position. Each position binds its own set, and every position - * also binds `clm_requester`: §04 makes that set every employee's default, the - * platform refuses to bind a set carrying a system permission to the - * `everyone` anchor (see `requester.profile.ts`), and the two leadership - * rungs hold no set of their own — the object-level read on `clm_contract` - * that lets `contract_executive_routed` / `contract_gm_routed` deliver rows - * comes from this binding. - * - * Catalog rows are per organization ("unique per organization"), so a - * position is looked up by name WITHOUT a limit of one and paired with the - * set row of the same organization; a single-tenant boot has one of each. - * Idempotent: a binding that already exists is left alone, so a restart never - * duplicates a row and an administrator's removal is not re-created behind - * their back within a boot. The log line at the end is the boot-time - * evidence that the grants landed. - */ - -const BINDINGS: ReadonlyArray = [ - [CLM_POSITION.legalCounsel, LegalSet.name], - [CLM_POSITION.legalHead, LegalSet.name], - [CLM_POSITION.financeController, FinanceSet.name], - [CLM_POSITION.recordsManager, RecordsSet.name], - [CLM_POSITION.admin, AdminSet.name], - ...Object.values(CLM_POSITION).map((position) => [position, RequesterSet.name] as const), -]; - -const SYS = { isSystem: true } as const; - -interface CatalogRow { - id?: string; - organization_id?: string | null; -} - -export interface BindHostContext { - ql: { - find: (object: string, query: unknown, options?: unknown) => Promise; - insert: (object: string, data: Record, options?: unknown) => Promise; - }; - logger?: { info?: (...a: unknown[]) => void; warn?: (...a: unknown[]) => void }; - hook?: (event: string, handler: () => Promise | void) => void; -} - -function rowsOf(result: unknown): CatalogRow[] { - if (Array.isArray(result)) return result as CatalogRow[]; - const records = (result as { records?: unknown })?.records; - return Array.isArray(records) ? (records as CatalogRow[]) : []; -} - -function organizationOf(row: CatalogRow): string | null { - return typeof row.organization_id === 'string' && row.organization_id !== '' ? row.organization_id : null; -} - -/** Every catalog row of that name, across organizations, under the system context the engine's read path expects. */ -async function findAllByName(ctx: BindHostContext, object: string, name: string): Promise { - try { - return rowsOf(await ctx.ql.find(object, { where: { name }, limit: 50, context: SYS })); - } catch (err) { - ctx.logger?.warn?.('[clm] position binding lookup failed', { - object, name, error: err instanceof Error ? err.message : String(err), - }); - return []; - } -} - -export function registerClmPositionBindings(ctx: BindHostContext): void { - const run = async (): Promise => { - let created = 0; - let existing = 0; - let skipped = 0; - for (const [positionName, setName] of BINDINGS) { - const positions = await findAllByName(ctx, 'sys_position', positionName); - const sets = await findAllByName(ctx, 'sys_permission_set', setName); - if (positions.length === 0 || sets.length === 0) { - ctx.logger?.warn?.('[clm] position binding skipped (row missing)', { position: positionName, set: setName }); - skipped += 1; - continue; - } - for (const position of positions) { - const organization = organizationOf(position); - const set = sets.find((candidate) => organizationOf(candidate) === organization); - if (!position.id || !set?.id) { - ctx.logger?.warn?.('[clm] position binding skipped (no set in the same organization)', { - position: positionName, set: setName, organization, - }); - skipped += 1; - continue; - } - const hit = rowsOf(await ctx.ql.find( - 'sys_position_permission_set', - { where: { position_id: position.id, permission_set_id: set.id }, limit: 1, context: SYS }, - ))[0]; - if (hit) { - existing += 1; - continue; - } - try { - await ctx.ql.insert( - 'sys_position_permission_set', - { - id: `ppsb_clm_${positionName}_${setName}${organization ? `_${organization.slice(0, 8)}` : ''}`, - position_id: position.id, - permission_set_id: set.id, - }, - { context: SYS }, - ); - created += 1; - } catch (err) { - ctx.logger?.warn?.('[clm] position binding insert failed', { - position: positionName, set: setName, error: err instanceof Error ? err.message : String(err), - }); - skipped += 1; - } - } - } - ctx.logger?.info?.('[clm] position bindings ensured', { created, existing, skipped, declared: BINDINGS.length }); - }; - - if (typeof ctx.hook === 'function') { - ctx.hook('kernel:bootstrapped', run); - } else { - void Promise.resolve().then(run); - } -} diff --git a/src/security/index.ts b/src/security/index.ts deleted file mode 100644 index 499aba9..0000000 --- a/src/security/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { registerClmPositionBindings } from './bind-position-sets.js'; -export type { BindHostContext } from './bind-position-sets.js'; diff --git a/src/sharing/positions.ts b/src/sharing/positions.ts index c3567b9..903d619 100644 --- a/src/sharing/positions.ts +++ b/src/sharing/positions.ts @@ -1,19 +1,30 @@ import { definePosition } from '@objectstack/spec/identity'; +import { AdminSet, FinanceSet, LegalSet, RecordsSet, RequesterSet } from '../profiles/index.js'; /** * The seven positions of DESIGN.md §04 — flat capability-distribution groups * (ADR-0090 D3): no parent, no hierarchy, no visibility roll-up. A position - * grants nothing by itself. Capability reaches it through the - * `sys_position_permission_set` bindings `src/security/bind-position-sets.ts` - * writes on `kernel:bootstrapped`, and rows reach it through the sharing rules - * in `contract.sharing.ts` that name the position as their recipient. + * grants nothing by itself. Capability reaches it through the permission sets + * its definition names in `permissionSets` (ADR-0131 D3/D4, protocol 18), and + * rows reach it through the sharing rules in `contract.sharing.ts` that name + * the position as their recipient. + * + * Every position names its own set (if it has one) AND `clm_requester`: §04 + * makes that set every employee's default, the platform refuses to bind a set + * carrying a system permission to the `everyone` anchor (see + * `requester.profile.ts`), and the two leadership rungs hold no set of their + * own — the object-level read on `clm_contract` that lets + * `contract_executive_routed` / `contract_gm_routed` deliver rows comes from + * this one. Up to 17.x the same pairs were `sys_position_permission_set` rows a + * boot-time binder wrote; 18 retired that object, and the binding is now part + * of the definition. * * There is deliberately no "employee" position. `clm_requester` is the set * every employee holds by default (§04), and a default is not a position — * see the distribution note at the top of `src/profiles/requester.profile.ts`. * - * The names are exported as constants so the sharing rules, the bindings and - * a future approval ladder (card 06) spell them from one place. + * The names are exported as constants so the sharing rules and the approval + * ladder (card 06) spell them from one place. */ export const CLM_POSITION = { legalCounsel: 'clm_legal_counsel', @@ -32,35 +43,42 @@ export const ClmPositions = [ name: CLM_POSITION.legalCounsel, label: 'Legal Counsel', description: 'Reviews and negotiates contracts. Reads and edits every contract through the legal sharing rule; accepts submissions and runs the review.', + permissionSets: [LegalSet.name, RequesterSet.name], }), definePosition({ name: CLM_POSITION.legalHead, label: 'Head of Legal', description: 'The legal rung of the approval ladder. Same contract reach as legal counsel; receives the review-overdue escalations.', + permissionSets: [LegalSet.name, RequesterSet.name], }), definePosition({ name: CLM_POSITION.financeController, label: 'Finance Controller', description: 'The finance rung of the approval ladder. Edits contracts from approved onward with the legal fields and the status locked; owns the payment schedule.', + permissionSets: [FinanceSet.name, RequesterSet.name], }), definePosition({ name: CLM_POSITION.executive, label: 'Executive', description: 'Reads the contracts routed to the executive rung of the approval ladder.', + permissionSets: [RequesterSet.name], }), definePosition({ name: CLM_POSITION.generalManager, label: 'General Manager', description: 'Reads the contracts routed to the general-manager rung of the approval ladder.', + permissionSets: [RequesterSet.name], }), definePosition({ name: CLM_POSITION.recordsManager, label: 'Records Manager', description: 'Execution registration, archive and the contract ledger: edits contracts from signing onward on the execution and archive fields only.', + permissionSets: [RecordsSet.name, RequesterSet.name], }), definePosition({ name: CLM_POSITION.admin, label: 'CLM Administrator', description: 'Maintains the configuration objects and holds full reach over every CLM object.', + permissionSets: [AdminSet.name, RequesterSet.name], }), ]; diff --git a/src/translations/en/flows.ts b/src/translations/en/flows.ts index c7df172..05489c3 100644 --- a/src/translations/en/flows.ts +++ b/src/translations/en/flows.ts @@ -9,28 +9,36 @@ * it. */ import type { TranslationData } from '@objectstack/spec/system'; +import { catalog } from './objects.catalog.js'; +import { contract } from './objects.contract.js'; + +// A select screen field draws its options from the object field it asks for +// (`optionsOf()` in the flow), so its option words are that field's words. +const contractOptions = (field: string) => contract.clm_contract!.fields![field]!.options!; export const flowSurface: Pick = { flows: { contract_intake: { label: 'Launch Contract', + successMessage: 'Contract launched.', screens: { screen_core: { title: 'Contract details', + description: 'The core terms every contract carries, then the fields this contract type asks for.', fields: { intakeFields: { label: 'Intake fields' }, title: { label: 'Title', placeholder: 'e.g. Mutual NDA with Acme' }, - our_entity: { label: 'Our signing entity' }, - department: { label: 'Requesting department' }, + our_entity: { label: 'Our signing entity', options: contractOptions('our_entity') }, + department: { label: 'Requesting department', options: contractOptions('department') }, amount: { label: 'Contract amount' }, - currency_code: { label: 'Currency' }, + currency_code: { label: 'Currency', options: contractOptions('currency_code') }, is_amount_estimated: { label: 'Amount is an estimate' }, start_date: { label: 'Start date' }, end_date: { label: 'End date' }, term_months: { label: 'Term (months)', placeholder: 'Instead of an end date' }, summary: { label: 'Summary', placeholder: 'What the deal is, in a few sentences' }, governing_law: { label: 'Governing law', placeholder: 'e.g. US-NY, England and Wales' }, - payment_terms: { label: 'Payment terms' }, + payment_terms: { label: 'Payment terms', options: contractOptions('payment_terms') }, confidentiality_term_months: { label: 'Confidentiality term (months)' }, auto_renew: { label: 'Auto-renews' }, parent_contract: { label: 'Parent contract (record id)', placeholder: 'The framework or main contract this one sits under' }, @@ -38,9 +46,10 @@ export const flowSurface: Pick = { }, screen_party: { title: 'New counterparty', + description: 'No counterparty was picked. Create one here — legal keeps its risk flag and screening up to date afterwards.', fields: { new_party_name: { label: 'Name' }, - new_party_kind: { label: 'Kind' }, + new_party_kind: { label: 'Kind', options: catalog.clm_party!.fields!.party_kind!.options! }, new_party_registration_no: { label: 'Registration / tax ID' }, new_party_contact_name: { label: 'Contact name' }, new_party_contact_email: { label: 'Contact email' }, @@ -48,20 +57,36 @@ export const flowSurface: Pick = { }, screen_document: { title: 'First version', + // The screen reads its sentence from the run: the type's template + // decides which of two `templateNote` assignments it shows. + description: '{{ templateNote }}', fields: { draft_from_template: { label: 'Draft from template' }, }, }, screen_upload: { title: 'Upload the first version', + description: 'The document this contract starts from. It becomes version 1.', }, screen_schedule: { title: 'Submit the contract', + description: 'Choose whether to submit now. Submitting routes the contract to legal review or to approval, as its type decides.', fields: { submit_now: { label: 'Submit now' }, }, }, }, }, + contract_approval: { + label: 'Contract Approval Ladder', + approvals: { + manager_review: { label: 'Direct Manager' }, + legal_finance_joint: { label: 'Head of Legal + Finance Controller (会签)' }, + legal_only: { label: 'Head of Legal' }, + finance_only: { label: 'Finance Controller' }, + executive_signoff: { label: 'Executive' }, + gm_signoff: { label: 'General Manager' }, + }, + }, }, }; diff --git a/src/translations/zh-CN/flows.ts b/src/translations/zh-CN/flows.ts index 339c4ee..9920bef 100644 --- a/src/translations/zh-CN/flows.ts +++ b/src/translations/zh-CN/flows.ts @@ -9,28 +9,36 @@ * not this bundle's. */ import type { TranslationData } from '@objectstack/spec/system'; +import { catalog } from './objects.catalog.js'; +import { contract } from './objects.contract.js'; + +// A select screen field draws its options from the object field it asks for +// (`optionsOf()` in the flow), so its option words are that field's words. +const contractOptions = (field: string) => contract.clm_contract!.fields![field]!.options!; export const flowSurface: Pick = { flows: { contract_intake: { label: '发起合同', + successMessage: '合同已发起。', screens: { screen_core: { title: '合同信息', + description: '先填每份合同都有的核心条款,再填这类合同要求的字段。', fields: { intakeFields: { label: '发起时填写的字段' }, title: { label: '标题', placeholder: '例如:与 Acme 的双向保密协议' }, - our_entity: { label: '我方签约主体' }, - department: { label: '发起部门' }, + our_entity: { label: '我方签约主体', options: contractOptions('our_entity') }, + department: { label: '发起部门', options: contractOptions('department') }, amount: { label: '合同金额' }, - currency_code: { label: '币种' }, + currency_code: { label: '币种', options: contractOptions('currency_code') }, is_amount_estimated: { label: '金额为估算值' }, start_date: { label: '生效日期' }, end_date: { label: '到期日期' }, term_months: { label: '期限(月)', placeholder: '可代替到期日期填写' }, summary: { label: '摘要', placeholder: '用几句话说明这笔交易' }, governing_law: { label: '适用法律', placeholder: '例如:US-NY、英格兰和威尔士' }, - payment_terms: { label: '付款条件' }, + payment_terms: { label: '付款条件', options: contractOptions('payment_terms') }, confidentiality_term_months: { label: '保密期限(月)' }, auto_renew: { label: '自动续约' }, parent_contract: { label: '主合同(记录 ID)', placeholder: '本合同所属的框架合同或主合同' }, @@ -38,9 +46,10 @@ export const flowSurface: Pick = { }, screen_party: { title: '新建相对方', + description: '没有选择相对方,在这里新建一个。之后由法务维护它的风险标记和筛查结果。', fields: { new_party_name: { label: '名称' }, - new_party_kind: { label: '类型' }, + new_party_kind: { label: '类型', options: catalog.clm_party!.fields!.party_kind!.options! }, new_party_registration_no: { label: '统一社会信用代码 / 税号' }, new_party_contact_name: { label: '联系人' }, new_party_contact_email: { label: '联系邮箱' }, @@ -48,20 +57,35 @@ export const flowSurface: Pick = { }, screen_document: { title: '首个版本', + // 说明文字取自运行时:合同类型有没有模板,决定显示哪一句 `templateNote`。 + description: '{{ templateNote }}', fields: { draft_from_template: { label: '从模板起草' }, }, }, screen_upload: { title: '上传首个版本', + description: '这份合同的起点文件,它会成为第 1 版。', }, screen_schedule: { title: '提交合同', + description: '选择是否现在提交。提交后,合同按类型的设置进入法务审查或审批。', fields: { submit_now: { label: '立即提交' }, }, }, }, }, + contract_approval: { + label: '合同审批', + approvals: { + manager_review: { label: '直属上级' }, + legal_finance_joint: { label: '法务负责人 + 财务负责人(会签)' }, + legal_only: { label: '法务负责人' }, + finance_only: { label: '财务负责人' }, + executive_signoff: { label: '分管领导' }, + gm_signoff: { label: '总经理' }, + }, + }, }, };