@acmekit/acmekit 2.13.83 → 2.13.84

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.
@@ -1,225 +1,301 @@
1
1
  ---
2
2
  name: write-test
3
- description: "Generates integration or unit tests using the correct AcmeKit test runner. Auto-detects whether to use pluginIntegrationTestRunner (full plugin tests) or moduleIntegrationTestRunner (module services). Use when adding tests for modules, workflows, API routes, or service methods."
3
+ description: "Generates integration or unit tests using the unified integrationTestRunner. Auto-detects the correct mode (plugin for container/HTTP, module for isolated services). Use when adding tests for modules, workflows, subscribers, jobs, or API routes."
4
4
  argument-hint: "[module-or-feature]"
5
5
  allowed-tools: Bash, Read, Write, Edit, Grep, Glob
6
6
  ---
7
7
 
8
8
  # Write Test (Plugin)
9
9
 
10
- Generate tests for AcmeKit plugins using the correct test runner and patterns.
11
-
12
- **CRITICAL:** Plugin tests have NO HTTP server. Do NOT generate tests using `api.get()`, `api.post()`, or `acmekitIntegrationTestRunner`. Test services directly through `container.resolve()`.
10
+ Generate tests for AcmeKit plugins using `integrationTestRunner` with the correct mode and patterns.
13
11
 
14
12
  ## Current Test Files
15
13
 
16
14
  - Existing tests: !`find src -name "*.spec.ts" -o -name "*.test.ts" 2>/dev/null | head -20 || echo "(none)"`
17
15
  - Plugin integration tests: !`ls integration-tests/plugin/*.spec.ts 2>/dev/null || echo "(none)"`
16
+ - HTTP integration tests: !`ls integration-tests/http/*.spec.ts 2>/dev/null || echo "(none)"`
18
17
 
19
18
  ## Critical Gotchas — Every Test Must Get These Right
20
19
 
21
- 1. **Service resolution key = module constant.** `container.resolve(BLOG_MODULE)` where `BLOG_MODULE = "blog"` (the string passed to `Module()`). NEVER guess `"blogModuleService"` — it won't resolve (AwilixResolutionError).
22
- 2. **`moduleName` in `moduleIntegrationTestRunner` = module constant.** Same rule: `moduleName: MY_MODULE` where `MY_MODULE` matches `Module()`. NEVER use `"myModuleService"`.
23
- 3. **`resolve` in `moduleIntegrationTestRunner` = string path from CWD.** Use `resolve: "./src/modules/my-module"`. NEVER pass the imported module object (`resolve: BlogModule` hangs — models not found).
24
- 4. **Workflow errors are plain objects, not Error instances.** NEVER use `.rejects.toThrow()` on workflows — always fails. Use `throwOnError: false` + `errors` array. Error path: `errors[0].error.message` (NOT `errors[0].message`).
25
- 5. **`.rejects.toThrow()` DOES work for service errors** (e.g., `service.retrievePost("bad-id")`). Only workflow errors are serialized.
26
- 6. **Schema sync, not migrations.** `pluginIntegrationTestRunner` and `moduleIntegrationTestRunner` sync schema from entities no migration files needed.
20
+ 1. **Unified runner only.** `import { integrationTestRunner } from "@acmekit/test-utils"`. NEVER use `pluginIntegrationTestRunner` or `moduleIntegrationTestRunner` — those are deprecated.
21
+ 2. **Service resolution key = module constant.** `container.resolve(GREETING_MODULE)` where `GREETING_MODULE = "greeting"` (the string passed to `Module()`). NEVER guess `"greetingModuleService"`.
22
+ 3. **`resolve` in module mode = absolute path.** Use `resolve: process.cwd() + "/src/modules/greeting"`. NEVER pass the imported module object.
23
+ 4. **Workflow errors are plain objects.** NEVER use `.rejects.toThrow()` on workflows. Use `throwOnError: false` + `errors` array. Error path: `errors[0].error.message`.
24
+ 5. **`.rejects.toThrow()` DOES work for service errors** (real `Error` instances). Only workflow errors are serialized.
25
+ 6. **Container-only vs HTTP.** Plugin mode without `http: true` has NO `api` fixture. NEVER use `api.get()` or `api.post()` in container-only tests.
26
+ 7. **HTTP mode requires auth.** `mode: "plugin"` + `http: true` boots the full framework — inline JWT + client API key setup in `beforeEach`.
27
+ 8. **Axios throws on 4xx/5xx.** Use `.catch((e: any) => e)` for error assertions in HTTP tests.
28
+ 9. **MockEventBusService emit takes arrays.** In plugin container mode, `emit` receives `[{ name, data }]` (array). Real event bus uses `{ name, data }` (single object).
27
29
 
28
30
  ## Instructions
29
31
 
30
32
  ### Step 1: Determine Test Type
31
33
 
32
- | What to test | Runner | File location |
34
+ | What to test | Mode | File location |
33
35
  |---|---|---|
34
- | Full plugin (all resources loaded) | `pluginIntegrationTestRunner` | `integration-tests/plugin/<feature>.spec.ts` |
35
- | Module service methods (isolated) | `moduleIntegrationTestRunner` | `src/modules/<mod>/__tests__/<name>.spec.ts` |
36
- | Workflows (through container) | `pluginIntegrationTestRunner` | `integration-tests/plugin/<feature>.spec.ts` |
37
- | Pure functions | Plain Jest `describe/it` | `src/**/__tests__/<name>.unit.spec.ts` |
36
+ | API routes (HTTP end-to-end) | `mode: "plugin"` + `http: true` | `integration-tests/http/<feature>.spec.ts` |
37
+ | Workflows, subscribers, jobs (container) | `mode: "plugin"` | `integration-tests/plugin/<feature>.spec.ts` |
38
+ | Module service methods (isolated) | `mode: "module"` | `src/modules/<mod>/__tests__/<name>.spec.ts` |
39
+ | Pure functions | Plain Jest | `src/**/__tests__/<name>.unit.spec.ts` |
38
40
 
39
41
  ### Step 2: Generate Test File
40
42
 
41
- #### Plugin Integration Test
43
+ #### Plugin HTTP Integration Test
42
44
 
43
45
  ```typescript
44
- import { pluginIntegrationTestRunner } from "@acmekit/test-utils"
45
- import { plugin } from "../../src/plugin"
46
- import { BLOG_MODULE } from "../../src/modules/blog" // BLOG_MODULE = "blog" — must match Module() key
47
-
48
- jest.setTimeout(60 * 1000)
49
-
50
- pluginIntegrationTestRunner({
46
+ import { integrationTestRunner } from "@acmekit/test-utils"
47
+ import {
48
+ ApiKeyType,
49
+ CLIENT_API_KEY_HEADER,
50
+ ContainerRegistrationKeys,
51
+ generateJwtToken,
52
+ Modules,
53
+ } from "@acmekit/framework/utils"
54
+ import { GREETING_MODULE } from "../../src/modules/greeting"
55
+
56
+ jest.setTimeout(120 * 1000)
57
+
58
+ integrationTestRunner({
59
+ mode: "plugin",
60
+ http: true,
51
61
  pluginPath: process.cwd(),
52
- pluginOptions: {
53
- apiKey: "test-api-key",
54
- },
55
- testSuite: ({ container, acmekitApp }) => {
56
- describe("Plugin loading", () => {
57
- it("should load plugin modules", () => {
58
- expect(acmekitApp.modules).toBeDefined()
62
+ pluginOptions: { apiKey: "test-api-key" },
63
+ testSuite: ({ api, container }) => {
64
+ let adminHeaders: Record<string, any>
65
+ let clientHeaders: Record<string, any>
66
+
67
+ beforeEach(async () => {
68
+ const userModule = container.resolve(Modules.USER)
69
+ const authModule = container.resolve(Modules.AUTH)
70
+ const apiKeyModule = container.resolve(Modules.API_KEY)
71
+
72
+ const user = await userModule.createUsers({
73
+ email: "admin@test.js",
59
74
  })
60
75
 
61
- it("should resolve typed plugin options", () => {
62
- const options = plugin.resolveOptions(container)
63
- expect(options.apiKey).toBe("test-api-key")
76
+ const authIdentity = await authModule.createAuthIdentities({
77
+ provider_identities: [
78
+ { provider: "emailpass", entity_id: "admin@test.js" },
79
+ ],
80
+ app_metadata: { user_id: user.id },
64
81
  })
65
- })
66
-
67
- describe("BlogModule CRUD", () => {
68
- let service: any
69
82
 
70
- beforeEach(() => {
71
- service = container.resolve(BLOG_MODULE) // resolves via module key
83
+ const config = container.resolve(
84
+ ContainerRegistrationKeys.CONFIG_MODULE
85
+ )
86
+ const { jwtSecret, jwtOptions } = config.projectConfig.http
87
+
88
+ const token = generateJwtToken(
89
+ {
90
+ actor_id: user.id,
91
+ actor_type: "user",
92
+ auth_identity_id: authIdentity.id,
93
+ app_metadata: { user_id: user.id },
94
+ },
95
+ { secret: jwtSecret, expiresIn: "1d", jwtOptions }
96
+ )
97
+
98
+ adminHeaders = {
99
+ headers: { authorization: `Bearer ${token}` },
100
+ }
101
+
102
+ const apiKey = await apiKeyModule.createApiKeys({
103
+ title: "Test Client Key",
104
+ type: ApiKeyType.CLIENT,
105
+ created_by: "test",
72
106
  })
73
107
 
74
- it("should create a blog post", async () => {
75
- const result = await service.createBlogPosts([
76
- { title: "Launch Announcement" },
77
- ])
78
- expect(result).toHaveLength(1)
79
- expect(result[0]).toEqual(
108
+ clientHeaders = {
109
+ headers: { [CLIENT_API_KEY_HEADER]: apiKey.token },
110
+ }
111
+ })
112
+
113
+ describe("POST /admin/plugin/greetings", () => {
114
+ it("should create a greeting", async () => {
115
+ const response = await api.post(
116
+ "/admin/plugin/greetings",
117
+ { message: "Hello from HTTP" },
118
+ adminHeaders
119
+ )
120
+ expect(response.status).toEqual(200)
121
+ expect(response.data.greeting).toEqual(
80
122
  expect.objectContaining({
81
123
  id: expect.any(String),
82
- title: "Launch Announcement",
124
+ message: "Hello from HTTP",
83
125
  })
84
126
  )
85
127
  })
86
128
 
87
- it("should list and count", async () => {
88
- await service.createBlogPosts([{ title: "A" }, { title: "B" }])
89
- const [posts, count] = await service.listAndCountBlogPosts()
90
- expect(count).toBe(2)
129
+ it("should reject missing required fields", async () => {
130
+ const { response } = await api
131
+ .post("/admin/plugin/greetings", {}, adminHeaders)
132
+ .catch((e: any) => e)
133
+ expect(response.status).toEqual(400)
134
+ })
135
+ })
136
+
137
+ describe("DELETE /admin/plugin/greetings/:id", () => {
138
+ it("should soft-delete and confirm", async () => {
139
+ const created = (
140
+ await api.post(
141
+ "/admin/plugin/greetings",
142
+ { message: "Delete me" },
143
+ adminHeaders
144
+ )
145
+ ).data.greeting
146
+
147
+ const response = await api.delete(
148
+ `/admin/plugin/greetings/${created.id}`,
149
+ adminHeaders
150
+ )
151
+ expect(response.data).toEqual({
152
+ id: created.id,
153
+ object: "greeting",
154
+ deleted: true,
155
+ })
91
156
  })
157
+ })
92
158
 
93
- it("should throw on missing required field", async () => {
94
- await expect(service.createBlogPosts([{}])).rejects.toThrow()
159
+ describe("Client routes", () => {
160
+ it("GET /client/plugin/greetings with API key", async () => {
161
+ await api.post(
162
+ "/admin/plugin/greetings",
163
+ { message: "Public" },
164
+ adminHeaders
165
+ )
166
+ const response = await api.get(
167
+ "/client/plugin/greetings",
168
+ clientHeaders
169
+ )
170
+ expect(response.status).toEqual(200)
171
+ expect(response.data.greetings).toHaveLength(1)
95
172
  })
96
173
  })
97
174
  },
98
175
  })
99
176
  ```
100
177
 
101
- **Fixtures:** `container` (proxy), `acmekitApp`, `MikroOrmWrapper`, `dbConfig`.
102
-
103
- #### Module Integration Test (Isolated)
178
+ #### Plugin Container Integration Test (workflows, subscribers, jobs)
104
179
 
105
180
  ```typescript
106
- import { moduleIntegrationTestRunner } from "@acmekit/test-utils"
107
- import { BLOG_MODULE } from "../../src/modules/blog" // BLOG_MODULE = "blog" — must match Module() key
181
+ import { integrationTestRunner } from "@acmekit/test-utils"
182
+ import { createGreetingsWorkflow } from "../../src/workflows"
183
+ import { GREETING_MODULE } from "../../src/modules/greeting"
108
184
 
109
- jest.setTimeout(30000)
185
+ jest.setTimeout(60 * 1000)
110
186
 
111
- moduleIntegrationTestRunner<IBlogModuleService>({
112
- moduleName: BLOG_MODULE, // must match Module() key
113
- resolve: "./src/modules/blog", // string path from CWD — NEVER pass imported module object
114
- testSuite: ({ service }) => {
115
- it("should create a blog post", async () => {
116
- const result = await service.createBlogPosts([
117
- { title: "Quarterly Report" },
118
- ])
119
- expect(result).toEqual([
120
- expect.objectContaining({ title: "Quarterly Report" }),
121
- ])
187
+ integrationTestRunner({
188
+ mode: "plugin",
189
+ pluginPath: process.cwd(),
190
+ pluginOptions: { apiKey: "test-api-key" },
191
+ testSuite: ({ container }) => {
192
+ it("should create via workflow", async () => {
193
+ const { result } = await createGreetingsWorkflow(container).run({
194
+ input: { greetings: [{ message: "Workflow Hello" }] },
195
+ })
196
+ expect(result).toHaveLength(1)
197
+ expect(result[0].message).toBe("Workflow Hello")
198
+ })
199
+
200
+ it("should reject invalid input", async () => {
201
+ const { errors } = await createGreetingsWorkflow(container).run({
202
+ input: { greetings: [{ message: "", status: "invalid" }] },
203
+ throwOnError: false,
204
+ })
205
+ expect(errors).toHaveLength(1)
206
+ expect(errors[0].error.message).toContain("Invalid")
122
207
  })
123
208
  },
124
209
  })
125
210
  ```
126
211
 
127
- **Fixtures:** `service` (proxy), `MikroOrmWrapper`, `acmekitApp`, `dbConfig`.
128
-
129
- #### Unit Test
212
+ #### Subscriber Test (direct handler invocation)
130
213
 
131
214
  ```typescript
132
- import { formatSlug } from "../format-slug"
215
+ import { integrationTestRunner } from "@acmekit/test-utils"
216
+ import greetingCreatedHandler from "../../src/subscribers/greeting-created"
217
+ import { GREETING_MODULE } from "../../src/modules/greeting"
218
+
219
+ jest.setTimeout(60 * 1000)
220
+
221
+ integrationTestRunner({
222
+ mode: "plugin",
223
+ pluginPath: process.cwd(),
224
+ testSuite: ({ container }) => {
225
+ it("should append ' [notified]' to greeting message", async () => {
226
+ const service: any = container.resolve(GREETING_MODULE)
227
+ const [greeting] = await service.createGreetings([
228
+ { message: "Hello World" },
229
+ ])
230
+
231
+ await greetingCreatedHandler({
232
+ event: {
233
+ data: { id: greeting.id },
234
+ name: "greeting.created",
235
+ },
236
+ container,
237
+ })
133
238
 
134
- describe("formatSlug", () => {
135
- it("should kebab-case the title", () => {
136
- expect(formatSlug("Hello World")).toBe("hello-world")
137
- })
239
+ const updated = await service.retrieveGreeting(greeting.id)
240
+ expect(updated.message).toBe("Hello World [notified]")
241
+ })
242
+ },
138
243
  })
139
244
  ```
140
245
 
141
- #### Testing Workflows Through Container
246
+ #### Job Test (direct invocation)
142
247
 
143
248
  ```typescript
144
- import { createBlogPostWorkflow } from "../../src/workflows"
145
-
146
- // Direct execution
147
- it("should execute the workflow", async () => {
148
- const { result } = await createBlogPostWorkflow(container).run({
149
- input: { title: "Workflow Test", author: "Admin" },
150
- })
151
- expect(result.blogPost).toEqual(
152
- expect.objectContaining({ title: "Workflow Test" })
153
- )
154
- })
249
+ import { integrationTestRunner } from "@acmekit/test-utils"
250
+ import cleanupGreetingsJob from "../../src/jobs/cleanup-greetings"
251
+ import { GREETING_MODULE } from "../../src/modules/greeting"
155
252
 
156
- it("should reject invalid workflow input", async () => {
157
- const { errors } = await createBlogPostWorkflow(container).run({
158
- input: {},
159
- throwOnError: false,
160
- })
161
- expect(errors).toHaveLength(1)
162
- expect(errors[0].error.message).toContain("title") // errors[0].error.message — NOT errors[0].message
163
- })
253
+ jest.setTimeout(60 * 1000)
164
254
 
165
- // Typed errors — PermanentFailure stops retries, SkipStep skips gracefully
166
- it("should permanently fail on invalid card", async () => {
167
- const { errors } = await chargeCardWorkflow(container).run({
168
- input: { cardToken: "invalid", amount: 100 },
169
- throwOnError: false,
170
- })
171
- expect(errors[0].error.message).toContain("Card declined")
172
- })
255
+ integrationTestRunner({
256
+ mode: "plugin",
257
+ pluginPath: process.cwd(),
258
+ testSuite: ({ container }) => {
259
+ it("should soft-delete greetings with lang='old'", async () => {
260
+ const service: any = container.resolve(GREETING_MODULE)
261
+
262
+ await service.createGreetings([
263
+ { message: "Old 1", lang: "old" },
264
+ { message: "Old 2", lang: "old" },
265
+ { message: "Current", lang: "en" },
266
+ ])
173
267
 
174
- it("should skip optional step when disabled", async () => {
175
- const { result } = await processOrderWorkflow(container).run({
176
- input: { items: [{ id: "item-1" }] },
177
- throwOnError: false,
178
- })
179
- // SkipStep doesn't produce errors — workflow continues
180
- expect(result).toBeDefined()
268
+ await cleanupGreetingsJob(container)
269
+
270
+ const remaining = await service.listGreetings()
271
+ expect(remaining).toHaveLength(1)
272
+ expect(remaining[0].lang).toBe("en")
273
+ })
274
+ },
181
275
  })
182
276
  ```
183
277
 
184
- #### Testing Domain Events
278
+ #### Module Integration Test (isolated)
185
279
 
186
280
  ```typescript
187
- import { MockEventBusService } from "@acmekit/test-utils"
188
-
189
- let eventBusSpy: jest.SpyInstance
190
- beforeEach(() => { eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") })
191
- afterEach(() => { eventBusSpy.mockRestore() })
192
-
193
- it("should emit blog-post.created event", async () => {
194
- const service = container.resolve(BLOG_MODULE) // "blog" — from Module() key
195
- await service.createBlogPosts([{ title: "Event Test" }])
196
- expect(eventBusSpy).toHaveBeenCalledWith(
197
- [expect.objectContaining({
198
- name: "blog-post.created",
199
- data: expect.objectContaining({ id: expect.any(String) }),
200
- })],
201
- { internal: true }
202
- )
203
- })
204
- ```
281
+ import { integrationTestRunner } from "@acmekit/test-utils"
205
282
 
206
- #### Testing Subscribers
283
+ jest.setTimeout(30000)
207
284
 
208
- ```typescript
209
- import { TestEventUtils } from "@acmekit/test-utils"
210
- import { Modules } from "@acmekit/framework/utils"
211
-
212
- it("should execute subscriber side-effect", async () => {
213
- const eventBus = container.resolve(Modules.EVENT_BUS)
214
- // IMPORTANT: capture promise BEFORE triggering event
215
- const subscriberExecution = TestEventUtils.waitSubscribersExecution(
216
- "blog-post.created",
217
- eventBus
218
- )
219
- const service = container.resolve(BLOG_MODULE) // "blog" — from Module() key
220
- await service.createBlogPosts([{ title: "Trigger Subscriber" }])
221
- await subscriberExecution
222
- // assert subscriber side-effect
285
+ integrationTestRunner<IGreetingModuleService>({
286
+ mode: "module",
287
+ moduleName: "greeting",
288
+ resolve: process.cwd() + "/src/modules/greeting",
289
+ testSuite: ({ service }) => {
290
+ it("should create a greeting", async () => {
291
+ const result = await service.createGreetings([
292
+ { message: "Hello" },
293
+ ])
294
+ expect(result[0]).toEqual(
295
+ expect.objectContaining({ message: "Hello" })
296
+ )
297
+ })
298
+ },
223
299
  })
224
300
  ```
225
301
 
@@ -228,16 +304,20 @@ it("should execute subscriber side-effect", async () => {
228
304
  ```bash
229
305
  pnpm test:unit # Unit tests
230
306
  pnpm test:integration:modules # Module integration tests
231
- pnpm test:integration:plugin # Full plugin integration tests
307
+ pnpm test:integration:plugin # Plugin container tests
308
+ pnpm test:integration:http # Plugin HTTP tests
232
309
  ```
233
310
 
234
311
  ## Key Patterns
235
312
 
313
+ - Use `integrationTestRunner` with `mode` — never the old deprecated runner names
236
314
  - Match the `jest.config.js` test buckets — don't invent new locations
237
- - All integration tests require `NODE_OPTIONS=--experimental-vm-modules`
238
- - Runners handle DB setup/teardown automatically — no manual cleanup needed
239
- - NEVER use `api.get()` or `api.post()` — plugin tests have no HTTP server
240
- - Always use `pluginPath: process.cwd()` unless the repo structure requires a different path
315
+ - Always use `pluginPath: process.cwd()` — never hardcode paths
316
+ - Container-only tests: access services via `container.resolve(MODULE_CONSTANT)` — no `api` fixture
317
+ - HTTP tests: full auth setup with `generateJwtToken` + `ApiKeyType.CLIENT` — no `createAdminUser` helper
318
+ - Pass body directly: `api.post(url, body, headers)` NOT `{ body: {...} }`
319
+ - Use `.catch((e: any) => e)` for error assertions — axios throws on 4xx/5xx
241
320
  - Use `expect.objectContaining()` with `expect.any(String)` for IDs/timestamps
242
- - Call `waitSubscribersExecution` BEFORE triggering the event
321
+ - Test subscribers by importing handler directly and calling with `{ event, container }`
322
+ - Test jobs by importing function directly and calling with `container`
243
323
  - Use realistic test data, not "test" or "foo"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acmekit/acmekit",
3
- "version": "2.13.83",
3
+ "version": "2.13.84",
4
4
  "description": "Generic application bootstrap and loaders for the AcmeKit framework",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -49,45 +49,45 @@
49
49
  "test:integration": "../../node_modules/.bin/jest --passWithNoTests --forceExit --testPathPattern=\"src/.*/integration-tests/__tests__/.*\\.ts\""
50
50
  },
51
51
  "devDependencies": {
52
- "@acmekit/framework": "2.13.83"
52
+ "@acmekit/framework": "2.13.84"
53
53
  },
54
54
  "dependencies": {
55
- "@acmekit/admin-bundler": "2.13.83",
56
- "@acmekit/analytics": "2.13.83",
57
- "@acmekit/analytics-local": "2.13.83",
58
- "@acmekit/analytics-posthog": "2.13.83",
59
- "@acmekit/api-key": "2.13.83",
60
- "@acmekit/auth": "2.13.83",
61
- "@acmekit/auth-emailpass": "2.13.83",
62
- "@acmekit/auth-github": "2.13.83",
63
- "@acmekit/auth-google": "2.13.83",
64
- "@acmekit/cache-inmemory": "2.13.83",
65
- "@acmekit/cache-redis": "2.13.83",
66
- "@acmekit/caching": "2.13.83",
67
- "@acmekit/caching-redis": "2.13.83",
68
- "@acmekit/core-flows": "2.13.83",
69
- "@acmekit/event-bus-local": "2.13.83",
70
- "@acmekit/event-bus-redis": "2.13.83",
71
- "@acmekit/file": "2.13.83",
72
- "@acmekit/file-local": "2.13.83",
73
- "@acmekit/file-s3": "2.13.83",
74
- "@acmekit/index": "2.13.83",
75
- "@acmekit/link-modules": "2.13.83",
76
- "@acmekit/locking": "2.13.83",
77
- "@acmekit/locking-postgres": "2.13.83",
78
- "@acmekit/locking-redis": "2.13.83",
79
- "@acmekit/notification": "2.13.83",
80
- "@acmekit/notification-local": "2.13.83",
81
- "@acmekit/notification-sendgrid": "2.13.83",
82
- "@acmekit/rbac": "2.13.83",
83
- "@acmekit/secrets-aws": "2.13.83",
84
- "@acmekit/secrets-local": "2.13.83",
85
- "@acmekit/settings": "2.13.83",
86
- "@acmekit/telemetry": "2.13.83",
87
- "@acmekit/translation": "2.13.83",
88
- "@acmekit/user": "2.13.83",
89
- "@acmekit/workflow-engine-inmemory": "2.13.83",
90
- "@acmekit/workflow-engine-redis": "2.13.83",
55
+ "@acmekit/admin-bundler": "2.13.84",
56
+ "@acmekit/analytics": "2.13.84",
57
+ "@acmekit/analytics-local": "2.13.84",
58
+ "@acmekit/analytics-posthog": "2.13.84",
59
+ "@acmekit/api-key": "2.13.84",
60
+ "@acmekit/auth": "2.13.84",
61
+ "@acmekit/auth-emailpass": "2.13.84",
62
+ "@acmekit/auth-github": "2.13.84",
63
+ "@acmekit/auth-google": "2.13.84",
64
+ "@acmekit/cache-inmemory": "2.13.84",
65
+ "@acmekit/cache-redis": "2.13.84",
66
+ "@acmekit/caching": "2.13.84",
67
+ "@acmekit/caching-redis": "2.13.84",
68
+ "@acmekit/core-flows": "2.13.84",
69
+ "@acmekit/event-bus-local": "2.13.84",
70
+ "@acmekit/event-bus-redis": "2.13.84",
71
+ "@acmekit/file": "2.13.84",
72
+ "@acmekit/file-local": "2.13.84",
73
+ "@acmekit/file-s3": "2.13.84",
74
+ "@acmekit/index": "2.13.84",
75
+ "@acmekit/link-modules": "2.13.84",
76
+ "@acmekit/locking": "2.13.84",
77
+ "@acmekit/locking-postgres": "2.13.84",
78
+ "@acmekit/locking-redis": "2.13.84",
79
+ "@acmekit/notification": "2.13.84",
80
+ "@acmekit/notification-local": "2.13.84",
81
+ "@acmekit/notification-sendgrid": "2.13.84",
82
+ "@acmekit/rbac": "2.13.84",
83
+ "@acmekit/secrets-aws": "2.13.84",
84
+ "@acmekit/secrets-local": "2.13.84",
85
+ "@acmekit/settings": "2.13.84",
86
+ "@acmekit/telemetry": "2.13.84",
87
+ "@acmekit/translation": "2.13.84",
88
+ "@acmekit/user": "2.13.84",
89
+ "@acmekit/workflow-engine-inmemory": "2.13.84",
90
+ "@acmekit/workflow-engine-redis": "2.13.84",
91
91
  "@inquirer/checkbox": "^2.3.11",
92
92
  "@inquirer/input": "^2.2.9",
93
93
  "boxen": "^5.0.1",
@@ -106,7 +106,7 @@
106
106
  },
107
107
  "peerDependencies": {
108
108
  "@acmekit/docs-bundler": "^2.13.42",
109
- "@acmekit/framework": "2.13.83",
109
+ "@acmekit/framework": "2.13.84",
110
110
  "@jimsheen/yalc": "^1.2.2",
111
111
  "@swc/core": "^1.7.28",
112
112
  "posthog-node": "^5.11.0",