create-ailk 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/component-catalog.json +3 -3
  2. package/dist/add-module.d.ts +7 -0
  3. package/dist/add-module.js +34 -2
  4. package/dist/add-route.js +18 -2
  5. package/dist/apply-design.d.ts +12 -1
  6. package/dist/apply-design.js +36 -4
  7. package/dist/cli.js +23 -3
  8. package/dist/derive-architecture.d.ts +15 -2
  9. package/dist/derive-architecture.js +34 -6
  10. package/dist/design-package.js +3 -2
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/lib/chrome-readers.d.ts +21 -0
  14. package/dist/lib/chrome-readers.js +100 -0
  15. package/dist/lib/extract-module-bundle.d.ts +11 -0
  16. package/dist/lib/extract-module-bundle.js +36 -6
  17. package/dist/lib/license-key-files.d.ts +6 -1
  18. package/dist/lib/license-key-files.js +9 -4
  19. package/dist/module-architecture.d.ts +9 -0
  20. package/dist/module-architecture.js +6 -0
  21. package/dist/parse-args.d.ts +22 -0
  22. package/dist/parse-args.js +73 -0
  23. package/dist/programmatic.d.ts +20 -0
  24. package/dist/programmatic.js +110 -9
  25. package/dist/site-chrome.d.ts +4 -0
  26. package/dist/site-chrome.js +34 -3
  27. package/dist/site-manifest.d.ts +12 -5
  28. package/dist/site-manifest.js +25 -11
  29. package/dist/sync-routes.js +11 -2
  30. package/dist/topology.d.ts +131 -0
  31. package/dist/topology.js +533 -0
  32. package/package.json +3 -3
  33. package/site-templates/designs/minimalism/DESIGN.md +3 -3
  34. package/templates/apps/api/CLAUDE.md +4 -2
  35. package/templates/apps/api/package.json +1 -0
  36. package/templates/apps/api/src/__tests__/factories/agent-caller.ts +5 -1
  37. package/templates/apps/api/src/lib/__mocks__/prisma.ts +10 -0
  38. package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +114 -1
  39. package/templates/apps/api/src/lib/__tests__/site.test.ts +54 -0
  40. package/templates/apps/api/src/lib/agent-store.ts +95 -3
  41. package/templates/apps/api/src/lib/site.ts +37 -0
  42. package/templates/apps/api/src/lib/spoke-store.ts +18 -2
  43. package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +4 -0
  44. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +22 -0
  45. package/templates/apps/api/src/middleware/auth.ts +3 -0
  46. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +23 -0
  47. package/templates/apps/api/src/openapi/spec.ts +38 -2
  48. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +50 -2
  49. package/templates/apps/api/src/routes/checkout/sessions.ts +7 -0
  50. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +275 -0
  51. package/templates/apps/api/src/routes/leads/index.ts +69 -6
  52. package/templates/apps/api/src/routes/webhooks/README.md +39 -0
  53. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +366 -0
  54. package/templates/apps/api/src/routes/webhooks/stripe.ts +61 -1
  55. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +199 -0
  56. package/templates/apps/api/src/services/default-tenant.ts +41 -7
  57. package/templates/apps/api/src/services/lead-routing.ts +148 -29
  58. package/templates/apps/api/src/services/public-site-resolver.ts +48 -0
  59. package/templates/apps/mcp/__tests__/index.test.ts +5 -5
  60. package/templates/apps/mcp/src/index.ts +6 -4
  61. package/templates/apps/mcp/src/lib/content-adapter.ts +2 -2
  62. package/templates/apps/mcp/src/lib/topology.generated.ts +24 -0
  63. package/templates/apps/web/CLAUDE.md +1 -0
  64. package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
  65. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
  66. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +66 -14
  67. package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +105 -11
  68. package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +12 -8
  69. package/templates/apps/web/app/[locale]/layout.tsx +71 -19
  70. package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +3 -0
  71. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +5 -2
  72. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +3 -0
  73. package/templates/apps/web/app/openapi.json/__tests__/route.test.ts +89 -1
  74. package/templates/apps/web/app/openapi.json/route.ts +69 -1
  75. package/templates/apps/web/instrumentation.ts +7 -4
  76. package/templates/apps/web/lib/__tests__/closing-footer-path.test.ts +96 -0
  77. package/templates/apps/web/lib/__tests__/relay.test.ts +290 -0
  78. package/templates/apps/web/lib/__tests__/site-brand.test.ts +52 -0
  79. package/templates/apps/web/lib/closing-footer-path.ts +50 -0
  80. package/templates/apps/web/lib/relay.ts +206 -2
  81. package/templates/apps/web/lib/site-brand.tsx +22 -1
  82. package/templates/apps/web/lib/site-chrome.ts +3 -0
  83. package/templates/apps/web/lib/topology.generated.ts +23 -0
  84. package/templates/apps/web/package.json +1 -1
  85. package/templates/database/CHANGELOG.md +25 -0
  86. package/templates/database/__tests__/agents.db.test.ts +65 -0
  87. package/templates/database/__tests__/lead-idempotency-key.db.test.ts +92 -0
  88. package/templates/database/auth/schema.prisma +23 -0
  89. package/templates/database/inbox/schema.prisma +13 -0
  90. package/templates/database/jest.config.cjs +7 -11
  91. package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +35 -0
  92. package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +20 -0
  93. package/templates/database/migrations/20261009120000_agent_grants/migration.sql +33 -0
  94. package/templates/database/package.json +1 -1
  95. package/versions.json +34 -33
@@ -106,6 +106,9 @@ model Workspace {
106
106
  spokes Spoke[]
107
107
  // #3591 — the agents bound to this workspace.
108
108
  agents Agent[]
109
+ // #7803 — the people who last activated this workspace (User.lastWorkspaceId).
110
+ // Read and written only by apps/api/src/services/tenant-context.ts.
111
+ lastActiveUsers User[] @relation("UserLastWorkspace")
109
112
 
110
113
  @@unique([organizationId, slug])
111
114
  @@index([organizationId])
@@ -167,6 +170,16 @@ enum AgentPermissionLevel {
167
170
  /// deleting the creator does not delete the agents they made for others.
168
171
  /// `apps/api/src/lib/agent-store.ts` is the only file that names
169
172
  /// `prisma.agent`.
173
+ ///
174
+ /// `grants` (#7864, spec docs/specs/7864-agent-key-grants-spec.md D2, D3)
175
+ /// lists the permissions an owner or admin granted this agent one by one —
176
+ /// only the five `@working-theory/validation` names in
177
+ /// `AGENT_GRANTABLE_PERMISSIONS` (content-edit, content-publish, assets-write,
178
+ /// magnets-manage, meeting-types-manage), held to that set by the
179
+ /// `agents_grants_allowed` CHECK. A grant never raises the ceiling above. The
180
+ /// agent holds a stored grant only while its person's own role holds it too:
181
+ /// the tenant guards clamp it on every request, so a demoted person's agent
182
+ /// keeps the row but loses the permission.
170
183
  model Agent {
171
184
  id String @id @default(dbgenerated("('agent_'::text || (uuidv7())::text)")) @db.Text
172
185
  organizationId String @map("organization_id")
@@ -174,6 +187,7 @@ model Agent {
174
187
  responsibleMembershipId String @map("responsible_membership_id")
175
188
  name String @db.VarChar(80)
176
189
  permissionLevel AgentPermissionLevel @default(member) @map("permission_level")
190
+ grants String[] @default([]) @map("grants")
177
191
  enabled Boolean @default(true)
178
192
  credentialHash String @unique @map("credential_hash") @db.Char(64)
179
193
  credentialPrefix String @map("credential_prefix") @db.VarChar(24)
@@ -374,7 +388,16 @@ model User {
374
388
  memberships Membership[]
375
389
  sentInvitations Invitation[] @relation("sent_invitations")
376
390
 
391
+ // #7803 — the workspace this person last activated, so a new sign-in lands
392
+ // there. Written only by activateWorkspace after its membership grant; read
393
+ // only by listTenancy, which reports it only when the workspace is in the
394
+ // caller's own listing (apps/api/src/services/tenant-context.ts). Deleting
395
+ // the workspace clears it. Never a Better Auth field, so no client can set it.
396
+ lastWorkspaceId String? @map("last_workspace_id")
397
+ lastWorkspace Workspace? @relation("UserLastWorkspace", fields: [lastWorkspaceId], references: [id], onDelete: SetNull)
398
+
377
399
  @@unique([billingProvider, billingCustomerId])
400
+ @@index([lastWorkspaceId])
378
401
  @@map("users")
379
402
  }
380
403
 
@@ -63,6 +63,19 @@ model Lead {
63
63
  promotedUserId String? @map("promoted_user_id")
64
64
  promotedAt DateTime? @map("promoted_at")
65
65
 
66
+ // #7869 (roadmap D5) — the caller's `Idempotency-Key` header on
67
+ // `POST /v1/leads/submit`, stored verbatim so a retried write finds the
68
+ // first lead. Written only by `apps/api/src/services/lead-routing.ts`'s
69
+ // submit branch and read only by its two scoped lookups. Untrusted,
70
+ // client-supplied text — never echo it. Nullable, no default: every
71
+ // pre-#7869 row and every keyless lead reads NULL, and Postgres treats
72
+ // NULLs as distinct under the unique below. Same shape as
73
+ // `FlowCheckout.idempotencyKey` (#4912).
74
+ idempotencyKey String? @map("idempotency_key")
75
+
76
+ // #7869 — the idempotency guarantee, scoped per workspace so a key can
77
+ // never reach another tenant's row.
78
+ @@unique([workspaceId, idempotencyKey])
66
79
  @@index([siteId])
67
80
  @@index([createdAt])
68
81
  @@index([email])
@@ -6,26 +6,22 @@ module.exports = {
6
6
  testPathIgnorePatterns: ["/node_modules/", "/generated/"],
7
7
  moduleFileExtensions: ["ts", "js", "json"],
8
8
  moduleNameMapper: {
9
- // Map the validation workspace package to its TypeScript source so
10
- // ts-jest compiles it directly (#3958) — mirrors apps/api/jest.config.cjs's
11
- // identical mapping. The published dist is ESM-only and this package's
12
- // CJS ts-jest transform can't load it; database/flows/presets.ts's test
13
- // needs the real flowConfigSchema value at runtime.
14
- "^@working-theory/validation$": "<rootDir>/../packages/validation/src/index.ts",
15
- // ../tenancy.ts's `isSentinel` import from @working-theory/ids became a
16
- // VALUE import (#5876 spec D2/D11) — no longer type-only, so it DOES need
17
- // a mapping now, same reason as the validation mapping above.
18
- "^@working-theory/ids$": "<rootDir>/../packages/ids/src/index.ts",
9
+ // @working-theory/validation and @working-theory/ids resolve from the
10
+ // installed packages; their ESM dist is converted by the transform below.
19
11
  "^(\\.{1,2}/.*)\\.js$": "$1",
20
12
  },
13
+ transformIgnorePatterns: [
14
+ "/node_modules/(?!(\\.pnpm/)?@working-theory(?:\\+[^/]*?@?[^/]*?|/[^/]+)(/|$))",
15
+ ],
21
16
  transform: {
22
- "^.+\\.tsx?$": [
17
+ "^.+\\.[cm]?[tj]sx?$": [
23
18
  "ts-jest",
24
19
  {
25
20
  tsconfig: {
26
21
  target: "ES2020",
27
22
  module: "commonjs",
28
23
  lib: ["ES2020"],
24
+ allowJs: true,
29
25
  declaration: false,
30
26
  strict: true,
31
27
  esModuleInterop: true,
@@ -0,0 +1,35 @@
1
+ -- #7803 — sign-in lands in the user's last workspace (spec
2
+ -- docs/specs/7803-sign-in-last-workspace-spec.md D1).
3
+ --
4
+ -- users.last_workspace_id (auth) — the workspace this person last activated.
5
+ -- Written only by activateWorkspace after its membership
6
+ -- grant; read only by listTenancy, which reports it only
7
+ -- when the workspace is in the caller's own listing
8
+ -- (apps/api/src/services/tenant-context.ts). Deleting the
9
+ -- workspace sets it null.
10
+ --
11
+ -- PURELY ADDITIVE: one nullable column with no default, its index, one FK to
12
+ -- "workspaces" and the ADR-0031 format CHECK on the new reference column. No
13
+ -- backfill runs and no existing row changes, so there is no pre-assertion
14
+ -- block — this migration cannot lose a row. A NULL passes the CHECK.
15
+ --
16
+ -- Rollout: deploy this migration BEFORE the API code that reads the column
17
+ -- (SEC-7803-3). Being nullable, it is safe to apply ahead of the code.
18
+ --
19
+ -- Rollback: ALTER TABLE "users" DROP COLUMN "last_workspace_id"; (this drops
20
+ -- the index, the FK and the CHECK with it). Safe while nothing but #7803's own
21
+ -- code reads it.
22
+
23
+ -- AlterTable
24
+ ALTER TABLE "users" ADD COLUMN "last_workspace_id" TEXT;
25
+
26
+ -- CreateIndex
27
+ CREATE INDEX "users_last_workspace_id_idx" ON "users"("last_workspace_id");
28
+
29
+ -- AddForeignKey
30
+ -- A deleted workspace clears every pointer to it.
31
+ ALTER TABLE "users" ADD CONSTRAINT "users_last_workspace_id_fkey" FOREIGN KEY ("last_workspace_id") REFERENCES "workspaces"("id") ON DELETE SET NULL ON UPDATE CASCADE;
32
+
33
+ -- ADR-0031 rule 3 — the reference column carries the format CHECK of the
34
+ -- entity it names (wksp_), in the exact pattern the agents migration uses.
35
+ ALTER TABLE "users" ADD CONSTRAINT "users_last_workspace_id_format" CHECK ("last_workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
@@ -0,0 +1,20 @@
1
+ -- #7869 (spoke-pattern roadmap Phase 1, D5) — Lead idempotency key: the
2
+ -- caller's optional `Idempotency-Key` header on POST /v1/leads/submit, so a
3
+ -- retried lead write (a client retry, a spoke's fulfilment replay) finds the
4
+ -- first lead instead of writing a second. Mirrors the column-add shape of
5
+ -- 20260906000000_add_flow_checkout_idempotency (one AlterTable, one unique
6
+ -- index under the Prisma section headers).
7
+ --
8
+ -- The column is nullable with no default, so no pre-existing row needs a
9
+ -- backfill and none collides on the new unique — Postgres treats NULLs as
10
+ -- distinct. The unique is scoped by workspace_id: one key in two workspaces
11
+ -- is two leads.
12
+ --
13
+ -- Rollback: ALTER TABLE "leads" DROP COLUMN "idempotency_key"; (the index
14
+ -- goes with it).
15
+
16
+ -- AlterTable
17
+ ALTER TABLE "leads" ADD COLUMN "idempotency_key" TEXT;
18
+
19
+ -- CreateIndex
20
+ CREATE UNIQUE INDEX "leads_workspace_id_idempotency_key_key" ON "leads"("workspace_id", "idempotency_key");
@@ -0,0 +1,33 @@
1
+ -- #7864 — grant an agent key five named abilities (spec
2
+ -- docs/specs/7864-agent-key-grants-spec.md D2).
3
+ --
4
+ -- agents.grants (auth) — the permissions an owner or admin granted this
5
+ -- agent one by one. Written only by setServedAgentGrants
6
+ -- (apps/api/src/lib/agent-store.ts, the PUT
7
+ -- …/agents/:agentId/grants route); read by the auth
8
+ -- preHandler's key lookup and the agent list. The tenant
9
+ -- guards hold an agent to the grants its person's own role
10
+ -- also holds, so a stored grant never outranks the person.
11
+ --
12
+ -- The CHECK names the same five permissions as @working-theory/validation's
13
+ -- AGENT_GRANTABLE_PERMISSIONS; database/__tests__/agents.db.test.ts reads
14
+ -- this file and fails if the two lists differ (SEC-7864-G).
15
+ --
16
+ -- PURELY ADDITIVE: one NOT NULL column with an empty-array default and its
17
+ -- CHECK. No backfill runs: every existing agent takes the default, no
18
+ -- grants, and so behaves exactly as before. No existing row changes beyond
19
+ -- the default fill, so there is no pre-assertion block.
20
+ --
21
+ -- Rollout: deploy this migration BEFORE the API code that reads the column.
22
+ -- Code without the column fails closed — its key lookup errors and every
23
+ -- agent is answered 401 (SEC-7864-F).
24
+ --
25
+ -- Rollback: ALTER TABLE "agents" DROP COLUMN "grants"; (this drops the CHECK
26
+ -- with it). Safe while nothing but #7864's own code reads it.
27
+
28
+ -- AlterTable
29
+ ALTER TABLE "agents" ADD COLUMN "grants" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[];
30
+
31
+ -- Only the five grantable permissions may be stored.
32
+ ALTER TABLE "agents" ADD CONSTRAINT "agents_grants_allowed"
33
+ CHECK ("grants" <@ ARRAY['content-edit','content-publish','assets-write','magnets-manage','meeting-types-manage']::TEXT[]);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@working-theory/database",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/versions.json CHANGED
@@ -1,52 +1,53 @@
1
1
  {
2
- "@working-theory/admin-ui": "0.36.0",
3
- "@working-theory/aeo": "0.36.0",
4
- "@working-theory/agent-context": "0.36.0",
2
+ "@working-theory/admin-ui": "0.37.0",
3
+ "@working-theory/aeo": "0.37.0",
4
+ "@working-theory/agent-context": "0.37.0",
5
5
  "@working-theory/agent-contract": "0.1.0",
6
6
  "@working-theory/agent-memory": "0.0.0",
7
7
  "@working-theory/ai": "0.1.0",
8
- "@working-theory/analytics": "0.36.0",
9
- "@working-theory/api-client": "0.36.0",
8
+ "@working-theory/analytics": "0.37.0",
9
+ "@working-theory/api-client": "0.37.0",
10
10
  "@working-theory/cda-filesystem": "0.0.0",
11
- "@working-theory/cli": "0.36.0",
12
- "@working-theory/common": "0.36.0",
11
+ "@working-theory/cli": "0.37.0",
12
+ "@working-theory/common": "0.37.0",
13
13
  "@working-theory/consent": "0.1.0",
14
- "@working-theory/content-adapters": "0.36.0",
15
- "@working-theory/content-projection": "0.36.0",
16
- "@working-theory/email": "0.36.0",
14
+ "@working-theory/content-adapters": "0.37.0",
15
+ "@working-theory/content-projection": "0.37.0",
16
+ "@working-theory/email": "0.37.0",
17
17
  "@working-theory/env": "0.0.0",
18
- "@working-theory/eslint-config": "0.36.0",
19
- "@working-theory/fumadocs-schema": "0.36.0",
18
+ "@working-theory/eslint-config": "0.37.0",
19
+ "@working-theory/fumadocs-schema": "0.37.0",
20
20
  "@working-theory/ids": "0.10.0",
21
- "@working-theory/jsonld": "0.36.0",
22
- "@working-theory/license": "0.36.0",
23
- "@working-theory/metadata": "0.36.0",
24
- "@working-theory/observability": "0.36.0",
25
- "@working-theory/page-renderer": "0.36.0",
21
+ "@working-theory/jsonld": "0.37.0",
22
+ "@working-theory/license": "0.37.0",
23
+ "@working-theory/metadata": "0.37.0",
24
+ "@working-theory/observability": "0.37.0",
25
+ "@working-theory/page-renderer": "0.37.0",
26
26
  "@working-theory/plugin-mcp": "0.1.2",
27
- "@working-theory/schema": "0.36.0",
28
- "@working-theory/schema-pack-registry": "0.36.0",
29
- "@working-theory/schema-validator": "0.36.0",
30
- "@working-theory/sections-registry": "0.36.0",
31
- "@working-theory/site-config": "0.36.0",
32
- "@working-theory/stripe-client": "0.36.0",
33
- "@working-theory/templates": "0.36.0",
34
- "@working-theory/theme": "0.36.0",
35
- "@working-theory/ui": "0.36.0",
36
- "@working-theory/validation": "0.36.0",
37
- "@working-theory/web-i18n": "0.36.0",
27
+ "@working-theory/schema": "0.37.0",
28
+ "@working-theory/schema-pack-registry": "0.37.0",
29
+ "@working-theory/schema-validator": "0.37.0",
30
+ "@working-theory/sections-registry": "0.37.0",
31
+ "@working-theory/site-config": "0.37.0",
32
+ "@working-theory/spoke-checkout": "0.2.0",
33
+ "@working-theory/stripe-client": "0.37.0",
34
+ "@working-theory/templates": "0.37.0",
35
+ "@working-theory/theme": "0.37.0",
36
+ "@working-theory/ui": "0.37.0",
37
+ "@working-theory/validation": "0.37.0",
38
+ "@working-theory/web-i18n": "0.37.0",
38
39
  "@working-theory/billing-client": "0.0.0",
39
40
  "@working-theory/branding": "0.0.0",
40
41
  "@working-theory/license-client": "0.0.0",
41
42
  "@working-theory/license-manifest": "0.0.0",
42
43
  "@working-theory/partner-session": "0.0.0",
43
44
  "@working-theory/preview-protocol": "0.0.0",
44
- "@working-theory/ui-commerce": "0.23.14",
45
- "@working-theory/ui-docs-blog": "0.23.14",
46
- "@working-theory/ui-scheduling": "0.23.14",
47
- "@working-theory/ui-signal-collectors": "0.25.3",
45
+ "@working-theory/ui-commerce": "0.23.15",
46
+ "@working-theory/ui-docs-blog": "0.23.15",
47
+ "@working-theory/ui-scheduling": "0.23.15",
48
+ "@working-theory/ui-signal-collectors": "0.25.4",
48
49
  "@working-theory/web": "0.0.0",
49
50
  "@working-theory/api": "1.0.0",
50
51
  "@working-theory/mcp": "0.0.0",
51
- "@working-theory/database": "0.36.0"
52
+ "@working-theory/database": "0.37.0"
52
53
  }