emulate 0.5.0 → 0.6.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 (45) hide show
  1. package/README.md +141 -19
  2. package/dist/api.js +460 -24
  3. package/dist/api.js.map +1 -1
  4. package/dist/{dist-PWGOAQC6.js → dist-2ZZGNPJI.js} +1 -1
  5. package/dist/dist-2ZZGNPJI.js.map +1 -0
  6. package/dist/{dist-4X2KPMAJ.js → dist-CXRPM6BK.js} +1 -3
  7. package/dist/dist-CXRPM6BK.js.map +1 -0
  8. package/dist/{dist-LDUHEJAN.js → dist-DSJSF3GY.js} +1 -3
  9. package/dist/dist-DSJSF3GY.js.map +1 -0
  10. package/dist/{dist-ETHHYBGF.js → dist-IFULY5LE.js} +1 -2
  11. package/dist/dist-IFULY5LE.js.map +1 -0
  12. package/dist/{dist-J6LHUR52.js → dist-IRUBHCZU.js} +1 -2
  13. package/dist/dist-IRUBHCZU.js.map +1 -0
  14. package/dist/{dist-ENKE2S7V.js → dist-NJJLJT2N.js} +1 -3
  15. package/dist/dist-NJJLJT2N.js.map +1 -0
  16. package/dist/dist-OGSAVJ25.js +4874 -0
  17. package/dist/dist-OGSAVJ25.js.map +1 -0
  18. package/dist/{dist-REDHDZ3V.js → dist-PO4CL5SJ.js} +1 -3
  19. package/dist/dist-PO4CL5SJ.js.map +1 -0
  20. package/dist/{dist-IBXD3O6A.js → dist-R3TNKUIE.js} +1 -3
  21. package/dist/dist-R3TNKUIE.js.map +1 -0
  22. package/dist/{dist-CFST4X4K.js → dist-WACHAAVU.js} +1 -2
  23. package/dist/dist-WACHAAVU.js.map +1 -0
  24. package/dist/{dist-5JVGPOL3.js → dist-XWWZVLQQ.js} +1 -2
  25. package/dist/dist-XWWZVLQQ.js.map +1 -0
  26. package/dist/{dist-KKTYBE5S.js → dist-ZY5SZSJ2.js} +8 -3
  27. package/dist/dist-ZY5SZSJ2.js.map +1 -0
  28. package/dist/index.js +464 -26
  29. package/dist/index.js.map +1 -1
  30. package/package.json +14 -15
  31. package/dist/chunk-AQ2CLRU3.js +0 -2146
  32. package/dist/chunk-AQ2CLRU3.js.map +0 -1
  33. package/dist/dist-4X2KPMAJ.js.map +0 -1
  34. package/dist/dist-5JVGPOL3.js.map +0 -1
  35. package/dist/dist-CE6BUCWQ.js +0 -1438
  36. package/dist/dist-CE6BUCWQ.js.map +0 -1
  37. package/dist/dist-CFST4X4K.js.map +0 -1
  38. package/dist/dist-ENKE2S7V.js.map +0 -1
  39. package/dist/dist-ETHHYBGF.js.map +0 -1
  40. package/dist/dist-IBXD3O6A.js.map +0 -1
  41. package/dist/dist-J6LHUR52.js.map +0 -1
  42. package/dist/dist-KKTYBE5S.js.map +0 -1
  43. package/dist/dist-LDUHEJAN.js.map +0 -1
  44. package/dist/dist-PWGOAQC6.js.map +0 -1
  45. package/dist/dist-REDHDZ3V.js.map +0 -1
package/README.md CHANGED
@@ -22,25 +22,25 @@ All services start with sensible defaults. No config file needed:
22
22
 
23
23
  ```bash
24
24
  # Start all services (zero-config)
25
- emulate
25
+ npx emulate
26
26
 
27
27
  # Start specific services
28
- emulate --service vercel,github
28
+ npx emulate --service vercel,github
29
29
 
30
30
  # Custom port
31
- emulate --port 3000
31
+ npx emulate --port 3000
32
32
 
33
33
  # Use a seed config file
34
- emulate --seed config.yaml
34
+ npx emulate --seed config.yaml
35
35
 
36
36
  # Generate a starter config
37
- emulate init
37
+ npx emulate init
38
38
 
39
39
  # Generate config for a specific service
40
- emulate init --service vercel
40
+ npx emulate init --service vercel
41
41
 
42
42
  # List available services
43
- emulate list
43
+ npx emulate list
44
44
  ```
45
45
 
46
46
  ### Options
@@ -64,7 +64,7 @@ The port can also be set via `EMULATE_PORT` or `PORT` environment variables.
64
64
  portless proxy start
65
65
 
66
66
  # Start emulate with portless integration
67
- emulate start --portless
67
+ npx emulate start --portless
68
68
  ```
69
69
 
70
70
  Each service registers as a portless alias and gets a named HTTPS URL:
@@ -82,7 +82,7 @@ The `--portless` flag overwrites any existing portless aliases matching `*.emula
82
82
  For a custom base URL without portless (any reverse proxy), use `--base-url` or the `EMULATE_BASE_URL` env var:
83
83
 
84
84
  ```bash
85
- emulate start --base-url "https://{service}.myproxy.test"
85
+ npx emulate start --base-url "https://{service}.myproxy.test"
86
86
  ```
87
87
 
88
88
  The `PORTLESS_URL` env var is automatically set by the `portless` CLI wrapper when running a command through it (e.g. `portless github.emulate emulate start`), typically to a value like `https://{service}.emulate.localhost`. It supports `{service}` interpolation, just like `--base-url` and `EMULATE_BASE_URL`. When no explicit `baseUrl` is provided, it is used as a fallback.
@@ -156,7 +156,7 @@ afterAll(() => Promise.all([github.close(), vercel.close()]))
156
156
 
157
157
  ## Configuration
158
158
 
159
- Configuration is optional. The CLI auto-detects config files in this order: `emulate.config.yaml` / `.yml`, `emulate.config.json`, `service-emulator.config.yaml` / `.yml`, `service-emulator.config.json`. Or pass `--seed <file>` explicitly. Run `emulate init` to generate a starter file.
159
+ Configuration is optional. The CLI auto-detects config files in this order: `emulate.config.yaml` / `.yml`, `emulate.config.json`, `service-emulator.config.yaml` / `.yml`, `service-emulator.config.json`. Or pass `--seed <file>` explicitly. Run `npx emulate init` to generate a starter file.
160
160
 
161
161
  ```yaml
162
162
  tokens:
@@ -246,6 +246,11 @@ slack:
246
246
  - name: developer
247
247
  real_name: Developer
248
248
  email: dev@example.com
249
+ profile:
250
+ title: Local Developer
251
+ status_text: Testing locally
252
+ status_emoji: ":computer:"
253
+ presence: active
249
254
  channels:
250
255
  - name: general
251
256
  topic: General discussion
@@ -256,9 +261,76 @@ slack:
256
261
  oauth_apps:
257
262
  - client_id: "12345.67890"
258
263
  client_secret: example_client_secret
264
+ app_id: A000000001
259
265
  name: My Slack App
260
266
  redirect_uris:
261
267
  - http://localhost:3000/api/auth/callback/slack
268
+ scopes:
269
+ - chat:write
270
+ - channels:read
271
+ - channels:history
272
+ - channels:join
273
+ - channels:manage
274
+ - channels:write
275
+ - groups:read
276
+ - groups:history
277
+ - groups:write
278
+ - im:read
279
+ - im:history
280
+ - im:write
281
+ - mpim:read
282
+ - mpim:history
283
+ - mpim:write
284
+ - users:read
285
+ - users:read.email
286
+ - users.profile:read
287
+ - users.profile:write
288
+ - users:write
289
+ - files:read
290
+ - files:write
291
+ - pins:read
292
+ - pins:write
293
+ - bookmarks:read
294
+ - bookmarks:write
295
+ - reactions:read
296
+ - reactions:write
297
+ - team:read
298
+ user_scopes: [users:read, users.profile:read]
299
+ bot_name: my-bot
300
+ tokens:
301
+ - token: xoxb-local-test
302
+ user: developer
303
+ scopes:
304
+ - chat:write
305
+ - channels:read
306
+ - channels:history
307
+ - channels:join
308
+ - channels:manage
309
+ - channels:write
310
+ - groups:read
311
+ - groups:history
312
+ - groups:write
313
+ - im:read
314
+ - im:history
315
+ - im:write
316
+ - mpim:read
317
+ - mpim:history
318
+ - mpim:write
319
+ - users:read
320
+ - users:read.email
321
+ - users.profile:read
322
+ - users.profile:write
323
+ - users:write
324
+ - files:read
325
+ - files:write
326
+ - pins:read
327
+ - pins:write
328
+ - bookmarks:read
329
+ - bookmarks:write
330
+ - reactions:read
331
+ - reactions:write
332
+ - team:read
333
+ strict_scopes: false
262
334
 
263
335
  apple:
264
336
  users:
@@ -598,39 +670,89 @@ OAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local
598
670
 
599
671
  ## Slack API
600
672
 
601
- Fully stateful Slack Web API emulation with channels, messages, threads, reactions, OAuth v2, and incoming webhooks.
673
+ Fully stateful Slack Web API emulation with channels, messages, threads, reactions, user profiles, presence, modern file uploads, pins, bookmarks, views, OAuth v2, and incoming webhooks. Chat writes preserve common rich message fields such as `blocks`, `attachments`, `metadata`, formatting flags, unfurl flags, and client message ids. Conversation writes update archive state, names, topics, purposes, membership, DMs, MPIMs, and read cursors. User writes update profile fields, status, custom fields, and deterministic active or away presence. File writes support the current external upload flow with local upload URLs, file share messages, reads, lists, downloads, and deletes. Pin and bookmark writes support channel message pins and link bookmarks. View writes support App Home publishing and modal stacks. Seeded OAuth apps and OAuth installs create bot users and installation records. OAuth exchanges and explicit token seeds create scoped token records. Supported write state changes dispatch Slack `event_callback` payloads to configured webhook URLs.
602
674
 
603
675
  ### Auth & Chat
604
676
  - `POST /api/auth.test` - test authentication
605
- - `POST /api/chat.postMessage` - post message (supports threads via `thread_ts`)
606
- - `POST /api/chat.update` - update message
677
+ - `POST /api/chat.postMessage` - post message with text or rich payload fields (supports threads via `thread_ts` and DM user IDs)
678
+ - `POST /api/chat.postEphemeral` - post ephemeral message outside channel history
679
+ - `POST /api/chat.update` - update message text and rich payload fields
607
680
  - `POST /api/chat.delete` - delete message
681
+ - `GET /api/chat.getPermalink` / `POST /api/chat.getPermalink` - get message permalink
682
+ - `POST /api/chat.scheduleMessage` - schedule pending message
683
+ - `POST /api/chat.deleteScheduledMessage` - delete pending scheduled message
684
+ - `POST /api/chat.scheduledMessages.list` - list pending scheduled messages
608
685
  - `POST /api/chat.meMessage` - /me message
609
686
 
610
687
  ### Conversations
611
- - `POST /api/conversations.list` - list channels (cursor pagination)
688
+ - `POST /api/conversations.list` - list conversations (cursor pagination, `types`, `exclude_archived`)
612
689
  - `POST /api/conversations.info` - get channel info
613
690
  - `POST /api/conversations.create` - create channel
614
- - `POST /api/conversations.history` - channel history
615
- - `POST /api/conversations.replies` - thread replies
691
+ - `POST /api/conversations.archive` / `conversations.unarchive` - archive/restore channel
692
+ - `POST /api/conversations.rename` - rename channel
693
+ - `POST /api/conversations.setTopic` / `conversations.setPurpose` - update topic/purpose
694
+ - `POST /api/conversations.history` - channel history with rich message fields
695
+ - `POST /api/conversations.replies` - thread replies with rich message fields
616
696
  - `POST /api/conversations.join` / `conversations.leave` - join/leave
697
+ - `POST /api/conversations.invite` / `conversations.kick` - manage membership
698
+ - `POST /api/conversations.open` / `conversations.close` - open/close DMs and MPIMs
699
+ - `POST /api/conversations.mark` - mark read cursor
617
700
  - `POST /api/conversations.members` - list members
618
701
 
619
702
  ### Users & Reactions
620
703
  - `POST /api/users.list` - list users (cursor pagination)
621
704
  - `POST /api/users.info` - get user info
622
705
  - `POST /api/users.lookupByEmail` - lookup by email
706
+ - `GET /api/users.profile.get` / `POST /api/users.profile.get` - get user profile fields
707
+ - `POST /api/users.profile.set` - update profile fields, status, and custom fields
708
+ - `GET /api/users.getPresence` / `POST /api/users.getPresence` - get active or away presence
709
+ - `POST /api/users.setPresence` - set the authed user to away or automatic presence
623
710
  - `POST /api/reactions.add` / `reactions.remove` / `reactions.get` - manage reactions
624
711
 
712
+ ### Files
713
+ - `POST /api/files.getUploadURLExternal` - create a local external upload session
714
+ - `POST /upload/v1/:fileId` - receive raw uploaded file bytes
715
+ - `POST /api/files.completeUploadExternal` - complete uploads and optionally share file messages
716
+ - `GET /api/files.info` / `POST /api/files.info` - get file metadata
717
+ - `GET /api/files.list` / `POST /api/files.list` - list completed files
718
+ - `GET /files-pri/:fileId/:filename` - download file bytes with a bearer token that can access the file
719
+ - `POST /api/files.delete` - delete a completed file
720
+
721
+ ### Pins & Bookmarks
722
+ - `POST /api/pins.add` - pin a message to a channel
723
+ - `GET /api/pins.list` / `POST /api/pins.list` - list pinned message items for a channel
724
+ - `POST /api/pins.remove` - remove a message pin from a channel
725
+ - `POST /api/bookmarks.add` - add a link bookmark to a channel
726
+ - `POST /api/bookmarks.edit` - update a link bookmark
727
+ - `POST /api/bookmarks.list` - list channel bookmarks
728
+ - `POST /api/bookmarks.remove` - remove a bookmark from a channel
729
+
730
+ ### Views
731
+ - `POST /api/views.publish` - publish or update an App Home view for a user
732
+ - `POST /api/views.open` - open a modal view
733
+ - `POST /api/views.update` - update a view by `view_id` or `external_id`
734
+ - `POST /api/views.push` - push a modal view onto the current modal stack
735
+ - `POST /api/views.generateTriggerId` - local helper for tests that need a modal trigger id
736
+
737
+ Modal opens and pushes require values from `/api/views.generateTriggerId`. Pass the returned value as `trigger_id` or `interactivity_pointer`; generate push values with an existing `view_id` and use them within 3 seconds.
738
+
625
739
  ### Team, Bots & Webhooks
626
740
  - `POST /api/team.info` - workspace info
627
741
  - `POST /api/bots.info` - bot info
628
- - `POST /services/:teamId/:botId/:webhookId` - incoming webhook
742
+ - `POST /services/:teamId/:botId/:webhookId` - incoming webhook with text or rich payload fields
629
743
 
630
744
  ### OAuth
631
745
  - `GET /oauth/v2/authorize` - authorization (shows user picker)
746
+ - `POST /oauth/v2/authorize/callback` - local user picker callback that creates the auth code
632
747
  - `POST /api/oauth.v2.access` - token exchange
633
748
 
749
+ ### Inspector
750
+ - `GET /` - tabbed local inspector for conversations, messages, files, views, auth records, incoming webhooks, event subscriptions, and event deliveries
751
+
752
+ Slack scope checks are relaxed by default so local tests can use simple bearer tokens. Set `slack.strict_scopes: true` in seed config to make supported Web API methods return Slack-style `missing_scope` errors with `needed` and `provided` fields. Strict mode checks `chat:write`, `channels:read`, `channels:history`, `channels:join`, `channels:manage`, `channels:write`, `groups:read`, `groups:history`, `groups:write`, `im:read`, `im:history`, `im:write`, `mpim:read`, `mpim:history`, `mpim:write`, `users:read`, `users:read.email`, `users.profile:read`, `users.profile:write`, `users:write`, `files:read`, `files:write`, `pins:read`, `pins:write`, `bookmarks:read`, `bookmarks:write`, `reactions:read`, `reactions:write`, and `team:read`. Slack lists no method-specific scopes for `views.publish`, `views.open`, `views.update`, or `views.push`, so the emulator requires auth but does not add strict-scope checks for those methods.
753
+
754
+ Current Slack limits: Slack Connect, Enterprise Grid admin APIs, Audit Logs API, SCIM, Legal Holds, Socket Mode, slash command and interaction simulation, user groups, reminders, stars, calls, canvases, lists, functions, workflows, chat streaming, legacy `files.upload`, exact rate limiting, and paid-plan behavior are not implemented.
755
+
634
756
  ## Apple Sign In
635
757
 
636
758
  Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
@@ -821,7 +943,7 @@ apps/
821
943
  web/ # Documentation site (Next.js)
822
944
  ```
823
945
 
824
- The core provides a generic `Store` with typed `Collection<T>` instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers its routes on the shared Hono app and uses the store for state.
946
+ The core provides a generic `Store` with typed `Collection<T>` instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers its routes with the shared internal app and uses the store for state.
825
947
 
826
948
  ## Auth
827
949
 
@@ -833,7 +955,7 @@ Tokens are configured in the seed config and map to users. Pass them as `Authori
833
955
 
834
956
  **Google**: Standard OAuth 2.0 authorization code flow. Configure clients in the seed config.
835
957
 
836
- **Slack**: All Web API endpoints require `Authorization: Bearer <token>`. OAuth v2 flow with user picker UI.
958
+ **Slack**: All Web API endpoints require `Authorization: Bearer <token>`. Seeded OAuth apps create local installation records, and OAuth v2 flow with user picker UI creates scoped bot tokens. Optional strict scope mode returns `missing_scope` when a token lacks a required method scope.
837
959
 
838
960
  **Apple**: OIDC authorization code flow with RS256 ID tokens. On first auth per user/client pair, a `user` JSON blob is included.
839
961