@terreno/agent-plugin 57.21.0 → 58.0.0-beta.3

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,32 +1,23 @@
1
1
  # Screen Patterns — Terreno Apps
2
2
 
3
- ## List screen
3
+ ## List screen (synced collection)
4
+
5
+ Synced collections read from the local store: no loading spinner after bootstrap, and
6
+ writes apply immediately.
4
7
 
5
8
  ```tsx
6
- import {Box, Button, Card, Heading, Page, Spinner, Text} from "@terreno/ui";
9
+ import {generateMutationId} from "@terreno/syncdb";
10
+ import {useMutate, useQuery} from "@terreno/syncdb/react";
11
+ import {Box, Button, Card, Heading, Page, Text} from "@terreno/ui";
7
12
  import {useCallback} from "react";
8
- import {useGetTodosQuery} from "@/store/sdk";
9
13
 
10
14
  const TodoListScreen: React.FC = () => {
11
- const {data, isLoading, error, refetch} = useGetTodosQuery({});
15
+ const todos = useQuery<Todo>("todos", {sort: "-created"});
16
+ const {create} = useMutate("todos");
12
17
 
13
18
  const handleCreate = useCallback((): void => {
14
- // navigate or open modal
15
- }, []);
16
-
17
- if (isLoading) {
18
- return <Page title="Todos"><Spinner /></Page>;
19
- }
20
- if (error) {
21
- return (
22
- <Page title="Todos">
23
- <Text color="error">Failed to load</Text>
24
- <Button text="Retry" onClick={refetch} />
25
- </Page>
26
- );
27
- }
28
-
29
- const items = data?.data ?? [];
19
+ create({data: {_id: generateMutationId(), title: "New todo", completed: false}});
20
+ }, [create]);
30
21
 
31
22
  return (
32
23
  <Page title="Todos" scroll>
@@ -35,11 +26,11 @@ const TodoListScreen: React.FC = () => {
35
26
  <Heading size="lg">Todos</Heading>
36
27
  <Button text="Add" onClick={handleCreate} iconName="plus" />
37
28
  </Box>
38
- {items.length === 0 ? (
29
+ {todos.length === 0 ? (
39
30
  <Text color="secondaryLight">No todos yet</Text>
40
31
  ) : (
41
- items.map((todo) => (
42
- <Card key={todo.id} padding={3}>
32
+ todos.map((todo) => (
33
+ <Card key={todo._id} padding={3}>
43
34
  <Text>{todo.title}</Text>
44
35
  </Card>
45
36
  ))
@@ -50,18 +41,20 @@ const TodoListScreen: React.FC = () => {
50
41
  };
51
42
  ```
52
43
 
44
+ Gate the empty state on the stream's bootstrap (see `docs/how-to/use-syncdb-correctly.md`).
45
+
53
46
  ## Profile screen
54
47
 
55
- Use `TapToEdit` for per-field profile edits. Save each field with `usePatchMeMutation` — do not collect every field behind one Save button.
48
+ Use `TapToEdit` for per-field profile edits. Save each field with the generated `usePatchAuthMeMutation` — do not collect every field behind one Save button.
56
49
 
57
50
  ```tsx
58
51
  import {Box, Card, Page, TapToEdit} from "@terreno/ui";
59
52
  import {useCallback, useEffect, useState} from "react";
60
- import {useGetMeQuery, usePatchMeMutation} from "@/store/sdk";
53
+ import {useGetAuthMeQuery, usePatchAuthMeMutation} from "@/store/apiSdk";
61
54
 
62
55
  const ProfileScreen: React.FC = () => {
63
- const {data: profile} = useGetMeQuery();
64
- const [updateProfile] = usePatchMeMutation();
56
+ const {data: profile} = useGetAuthMeQuery();
57
+ const [updateProfile] = usePatchAuthMeMutation();
65
58
  const [name, setName] = useState<string>("");
66
59
 
67
60
  // Seed only this field from the server so saving another TapToEdit does not wipe the draft.
@@ -100,29 +93,26 @@ const ProfileScreen: React.FC = () => {
100
93
  ## Form screen
101
94
 
102
95
  ```tsx
96
+ import {generateMutationId} from "@terreno/syncdb";
97
+ import {useMutate} from "@terreno/syncdb/react";
103
98
  import {Box, Button, Page, TextField} from "@terreno/ui";
104
99
  import {useCallback, useState} from "react";
105
100
  import {useRouter} from "expo-router";
106
- import {usePostTodosMutation} from "@/store/sdk";
107
101
 
108
102
  const CreateTodoScreen: React.FC = () => {
109
103
  const router = useRouter();
110
104
  const [title, setTitle] = useState<string>("");
111
105
  const [titleError, setTitleError] = useState<string | undefined>();
112
- const [createTodo, {isLoading}] = usePostTodosMutation();
106
+ const {create} = useMutate("todos");
113
107
 
114
- const handleSubmit = useCallback(async (): Promise<void> => {
108
+ const handleSubmit = useCallback((): void => {
115
109
  if (!title.trim()) {
116
110
  setTitleError("Title is required");
117
111
  return;
118
112
  }
119
- try {
120
- await createTodo({title: title.trim(), completed: false}).unwrap();
121
- router.back();
122
- } catch (err) {
123
- console.error("Create failed", err);
124
- }
125
- }, [createTodo, router, title]);
113
+ create({data: {_id: generateMutationId(), title: title.trim(), completed: false}});
114
+ router.back();
115
+ }, [create, router, title]);
126
116
 
127
117
  return (
128
118
  <Page title="New Todo" scroll>
@@ -133,28 +123,28 @@ const CreateTodoScreen: React.FC = () => {
133
123
  onChange={setTitle}
134
124
  errorText={titleError}
135
125
  />
136
- <Button text="Save" onClick={handleSubmit} loading={isLoading} fullWidth />
126
+ <Button text="Save" onClick={handleSubmit} fullWidth />
137
127
  </Box>
138
128
  </Page>
139
129
  );
140
130
  };
141
131
  ```
142
132
 
133
+ Server rejections arrive later in `useConflicts()` / `useSyncStatus().failedCount`, not
134
+ from `create`.
135
+
143
136
  ## Detail screen with params
144
137
 
145
138
  ```tsx
146
- import {Page, Spinner, Text} from "@terreno/ui";
139
+ import {useEntity} from "@terreno/syncdb/react";
140
+ import {Page, Text} from "@terreno/ui";
147
141
  import {useLocalSearchParams} from "expo-router";
148
- import {useGetTodosByIdQuery} from "@/store/sdk";
149
142
 
150
143
  const TodoDetailScreen: React.FC = () => {
151
144
  const {id} = useLocalSearchParams<{id: string}>();
152
- const {data, isLoading, error} = useGetTodosByIdQuery({id: id ?? ""}, {skip: !id});
145
+ const {data, deleted} = useEntity<Todo>("todos", id ?? "");
153
146
 
154
- if (isLoading) {
155
- return <Page title="Todo"><Spinner /></Page>;
156
- }
157
- if (error || !data) {
147
+ if (!data || deleted) {
158
148
  return <Page title="Todo"><Text color="error">Not found</Text></Page>;
159
149
  }
160
150
 
@@ -175,17 +165,10 @@ skill `building-admin-interfaces`.
175
165
  ```tsx
176
166
  import {AdminScreenRouter} from "@terreno/admin-frontend";
177
167
  import {useLocalSearchParams} from "expo-router";
178
- import {terrenoApi} from "@/store/sdk";
179
168
 
169
+ // Rendered inside the AdminProvider in app/admin/_layout.tsx.
180
170
  const AdminModelScreen: React.FC = () => {
181
171
  const {model} = useLocalSearchParams<{model: string}>();
182
- return (
183
- <AdminScreenRouter
184
- api={terrenoApi}
185
- apiBase="/admin"
186
- name={model ?? ""}
187
- routeBase="/admin"
188
- />
189
- );
172
+ return <AdminScreenRouter apiBase="/admin" name={model ?? ""} routeBase="/admin" />;
190
173
  };
191
174
  ```
@@ -1,65 +1,64 @@
1
1
  ---
2
2
  name: generate-sdk
3
3
  description: >-
4
- Regenerate the example-frontend RTK Query SDK (store/openApiSdk.ts) from the
5
- example-backend's OpenAPI spec. Handles dependency install, backend startup,
6
- SDK codegen, and cleanup from a fresh checkout. Use whenever the backend's API
7
- surface changes — new modelRouter routes, custom routes registered via
8
- createOpenApiBuilder, new/modified Mongoose models or field descriptions in
9
- example-backend, changes to
4
+ Regenerate the example-frontend SDKs from the example-backend's OpenAPI spec
5
+ with terreno-syncdb-codegen: store/apiSdk.ts (typed request hooks for every
6
+ non-synced route, `bun run sdk`) and store/syncDbSdk.ts (synced collection
7
+ hooks, `bun run sync-sdk`). Handles dependency install, backend startup,
8
+ codegen, and cleanup from a fresh checkout. Use whenever the backend's API
9
+ surface changes — new modelRouter routes or actions, custom routes registered
10
+ via createOpenApiBuilder, new/modified Mongoose models or field descriptions
11
+ in example-backend, sync config changes, changes to
10
12
  permissions/queryFields/populatePaths/responseHandler, edits to @terreno/api
11
13
  routing or OpenAPI generation, or any time a route is added/removed/renamed.
12
- Also use when the user asks to "regenerate the SDK", "update openApiSdk.ts",
13
- "run bun run sdk", or reports stale/missing generated hooks. Lifecycle
14
- composition: Pick after API-shape changes; Roast to verify generated-client
15
- compatibility.
14
+ Also use when the user asks to "regenerate the SDK", "update apiSdk.ts" or
15
+ "syncDbSdk.ts", "run bun run sdk" or "bun run sync-sdk", or reports
16
+ stale/missing generated hooks. Lifecycle composition: Pick after API-shape
17
+ changes; Roast to verify generated-client compatibility.
16
18
  targets:
17
19
  - '*'
18
20
  ---
19
21
  # Generate Frontend SDK
20
22
 
21
- Regenerates `example-frontend/store/openApiSdk.ts` from the backend OpenAPI spec via RTK Query codegen.
23
+ Regenerates the two generated files in `example-frontend` from the live backend spec at
24
+ `http://localhost:4000/openapi.json` (or `OPENAPI_URL`). Both come from
25
+ `terreno-syncdb-codegen` in `@terreno/syncdb`.
22
26
 
23
- **Scope:** This skill covers **non-synced** routes only (auth, profile, AI,
24
- feature flags, custom RPC). Synced collections use `@terreno/syncdb` hooks
25
- (`useQuery`, `useMutate`) — they are **not** in the generated SDK. Built-in
26
- admin RPC uses admin-frontend's host-bound fetch client; do not add admin
27
- `injectEndpoints`. See [syncdb reference](../../../../docs/reference/syncdb.md)
28
- and [migrate-rtk-to-syncdb.md](../../../../docs/how-to/migrate-rtk-to-syncdb.md).
27
+ | Command | Output | Covers |
28
+ |---------|--------|--------|
29
+ | `bun run sdk` (runs `api-sdk`) | `store/apiSdk.ts` | Every operation without `x-terreno-sync`: `/auth/me`, modelRouter actions, AI, consents, announcements, version check |
30
+ | `bun run sync-sdk` | `store/syncDbSdk.ts` | `SYNC_COLLECTIONS` + collection hooks for the collections in the script's `--collections` list |
29
31
 
30
- ### Legacy RTK data path
31
-
32
- During the `@terreno/rtk` support window, some screens may still use generated `useGetXQuery` / `usePostXMutation` for collections. Do not add new collection hooks — migrate to syncdb instead.
33
-
34
- The `example-frontend` consumes the `example-backend` API via auto-generated RTK Query hooks in `store/openApiSdk.ts`. The codegen pulls from the live backend at `http://localhost:4000/openapi.json`, so the backend must be running while `bun run sdk` executes.
35
-
36
- **Never edit `example-frontend/store/openApiSdk.ts` by hand — it is fully overwritten by this skill.**
32
+ **Never edit `store/apiSdk.ts` or `store/syncDbSdk.ts` by hand — codegen overwrites them.**
33
+ Codegen options live in `example-frontend/syncdb-codegen.json` (`excludePaths` drops
34
+ routes such as the SSE streams; `overrides` sets per-collection options). Built-in admin
35
+ uses admin-frontend's own fetch client and is not generated here.
37
36
 
38
37
  ## Documentation
39
38
 
40
- Read `docs/reference/syncdb.md` and the backend/OpenAPI reference before regenerating. After a public API change, run `update-docs` in the same slice so reference and how-to pages match the new hooks. Generated SDK files are not hand-edited docs.
39
+ Read `docs/reference/syncdb.md#codegen` and `docs/how-to/generate-the-client-sdk.md`
40
+ before regenerating. After a public API change, run `update-docs` in the same slice.
41
41
 
42
42
  ## When to run this skill
43
43
 
44
- Run it whenever the backend's OpenAPI surface changes. Common triggers:
44
+ Run `sdk` when the non-synced OpenAPI surface changes; run `sync-sdk` when a synced
45
+ collection's schema or `sync` config changes. Run both when unsure. Triggers:
45
46
 
46
- - New or removed `modelRouter` registration
47
- - Edits to `permissions`, `queryFields`, `populatePaths`, `defaultQueryParams`, `sort`, `responseHandler`, or `openApiOverwrite` on an existing `modelRouter`
48
- - New custom routes built with `createOpenApiBuilder` (path params, query params, request body, response schema changes)
49
- - Mongoose schema changes that affect the public shape: added/removed fields, type changes, required/enum/default changes, `description` updates, ref changes
50
- - Changes to auth routes (e.g. `addAuthRoutes`, `addMeRoutes`, GitHub OAuth, Better Auth)
51
- - Edits to `@terreno/api` files that produce the OpenAPI spec (`openApi.ts`, `openApiBuilder.ts`, `api.ts`, `populate.ts`)
52
- - User asks: "regenerate SDK", "update the SDK", "run bun run sdk", "openApiSdk.ts is out of date"
47
+ - New or removed `modelRouter` registration, `collectionActions`, or `instanceActions`
48
+ - Edits to `permissions`, `queryFields`, `populatePaths`, `defaultQueryParams`, `sort`, `responseHandler`, `sync`, or `openApiOverwrite`
49
+ - New custom routes built with `createOpenApiBuilder`
50
+ - Mongoose schema changes that affect the public shape (fields, types, required/enum/default, `description`, refs)
51
+ - Auth route changes (`addAuthRoutes`, `addMeRoutes`, GitHub OAuth, Better Auth)
52
+ - Edits to `@terreno/api` OpenAPI generation (`openApi.ts`, `openApiBuilder.ts`, `api.ts`, `populate.ts`) or to `syncdb/src/codegen/`
53
+ - A new synced collection: also add it to the `--collections` list in the `sync-sdk` script
53
54
 
54
- If the change is purely backend-internal (logging, refactor that doesn't alter routes/models, test-only edits), the SDK does **not** need regeneration — skip this skill.
55
+ Skip it for backend-internal changes (logging, refactors that keep routes and models, tests).
55
56
 
56
57
  ## Prerequisites
57
58
 
58
- - Bun is on PATH (run `bun` directly; do not modify PATH)
59
- - MongoDB reachable at `mongodb://localhost:27017` (the example backend connects on boot)
60
- - Ports 4000 (backend) must be free
61
-
62
- If MongoDB is not running, tell the user and stop — do not try to install or start it automatically.
59
+ - Bun on PATH (run `bun` directly; do not modify PATH)
60
+ - A MongoDB **replica set** at `MONGO_URI` (the backend's change streams need one)
61
+ - Port 4000 free
63
62
 
64
63
  ## Procedure
65
64
 
@@ -67,90 +66,100 @@ Run the steps in order. Stop and report on the first failure.
67
66
 
68
67
  ### 1. Verify the worktree is bootstrapped
69
68
 
70
- Detect whether dependencies are installed. The repo uses Bun workspaces, so a single root `node_modules` is sufficient.
71
-
72
69
  ```bash
73
70
  test -d node_modules && test -d example-backend/node_modules && test -d example-frontend/node_modules && echo "installed" || echo "missing"
74
71
  ```
75
72
 
76
- If `missing`, run the full bootstrap from the repo root. This installs deps **and** compiles every workspace package (needed because `example-frontend` imports built artifacts from `@terreno/rtk` and other workspaces):
73
+ If `missing`, run `bun run bootstrap` from the repo root in the background and wait for it.
74
+ It installs deps and compiles every workspace package.
75
+
76
+ ### 2. Start MongoDB if it is not running
77
+
78
+ `mongosh --quiet --eval 'rs.status().ok'` (or a `node` MongoClient ping) succeeds when a
79
+ replica set is up. Otherwise start one from the `mongodb-memory-server` binary cache and
80
+ initiate it once, as in `/workspace/CLAUDE.md` ("Example full stack"):
77
81
 
78
82
  ```bash
79
- bun run bootstrap
83
+ mkdir -p .devdata/mongo
84
+ "$(ls ~/.cache/mongodb-binaries/mongod-* | head -1)" \
85
+ --replSet rs0 --port 27017 --bind_ip 127.0.0.1 --dbpath .devdata/mongo
80
86
  ```
81
87
 
82
- Bootstrap can take a few minutes — run it in the background and wait for it to finish before continuing.
88
+ Run `mongod` in the background. Initiate the replica set with `node` (not `bun`) from
89
+ `example-backend/`. If no `mongod` binary exists, tell the user and stop.
83
90
 
84
- ### 2. Ensure backend env vars are set
85
-
86
- The backend reads `example-backend/.env`. If that file is missing, copy the example:
91
+ ### 3. Ensure backend env vars are set
87
92
 
88
93
  ```bash
89
94
  test -f example-backend/.env || cp example-backend/.env.example example-backend/.env
90
95
  ```
91
96
 
92
- The default `.env.example` values are fine for local SDK generation. Do not invent secrets or overwrite an existing `.env`.
93
-
94
- ### 3. Start the example backend on port 4000
97
+ The example values (`MONGO_URI=mongodb://127.0.0.1:27017/terreno-example?replicaSet=rs0`,
98
+ token and session secrets) are fine for codegen. Never overwrite an existing `.env`.
95
99
 
96
- The dev script watches files, which is fine for a one-shot codegen run. Start it in the background from the repo root:
100
+ ### 4. Start the example backend on port 4000
97
101
 
98
102
  ```bash
99
103
  bun run backend:dev
100
104
  ```
101
105
 
102
- Use the Bash tool's `run_in_background: true` so the process keeps running while you continue.
106
+ Use `run_in_background: true`.
103
107
 
104
- ### 4. Wait for the OpenAPI spec to be served
108
+ ### 5. Wait for the OpenAPI spec
105
109
 
106
- Poll `http://localhost:4000/openapi.json` until it returns HTTP 200. The backend has to connect to MongoDB and register all routes before the spec is available, which can take 5–20 seconds on a cold start. Do not skip this wait — running `bun run sdk` before the spec is ready produces an empty/broken `openApiSdk.ts`.
110
+ Poll until the spec returns 200 (5–20 s on a cold start). Codegen against a backend that is
111
+ not ready fails or emits an empty SDK.
107
112
 
108
113
  ```bash
109
- until curl -sf -o /dev/null -w "%{http_code}" http://localhost:4000/openapi.json | grep -q 200; do sleep 2; done
114
+ until curl -sf -o /dev/null http://localhost:4000/openapi.json; do sleep 2; done
110
115
  ```
111
116
 
112
- If the backend logs an error (Mongo connection refused, port already in use, compile error), stop and surface the error to the user. Do not retry blindly.
113
-
114
- ### 5. Generate the SDK
117
+ If the backend logs an error (Mongo connection refused, port in use, compile error), stop
118
+ and report it.
115
119
 
116
- Run the codegen from `example-frontend`. The script also runs Biome formatting on the output and strips an empty re-export line, so no follow-up formatting is needed.
120
+ ### 6. Generate
117
121
 
118
122
  ```bash
119
- cd example-frontend && bun run sdk
123
+ cd example-frontend && bun run sdk && bun run sync-sdk
120
124
  ```
121
125
 
122
- If the script fails with a missing module error (e.g. `@rtk-query/codegen-openapi`), dependencies are stale — re-run `bun run bootstrap` from the repo root and try again.
126
+ Both scripts format their output with Biome. A non-zero exit names the problem: two
127
+ operations sharing a hook name (give one an `operationId`), a missing collection path, or a
128
+ list response without `data.items`.
123
129
 
124
- ### 6. Stop the backend
130
+ ### 7. Stop the backend
125
131
 
126
- Kill the background backend process you started in step 3. Use the PID from the background shell, or:
132
+ Kill the background backend you started (and `mongod`, if you started it):
127
133
 
128
134
  ```bash
129
135
  pkill -f "example-backend.*src/index.ts" || true
130
136
  ```
131
137
 
132
- ### 7. Verify and report
138
+ ### 8. Verify and report
133
139
 
134
- Confirm `example-frontend/store/openApiSdk.ts` exists and was modified. Show the user a short summary:
135
-
136
- - Which routes/models triggered the regeneration
137
- - A `git diff --stat example-frontend/store/openApiSdk.ts` to show the size of the change
138
- - Any warnings or errors emitted by the backend or codegen
140
+ ```bash
141
+ git diff --stat example-frontend/store/apiSdk.ts example-frontend/store/syncDbSdk.ts
142
+ cd example-frontend && bunx tsc --noEmit
143
+ ```
139
144
 
140
- Do **not** commit `openApiSdk.ts` automatically — leave that to the user's normal commit flow.
145
+ Report which backend changes triggered the run, the diff stat, and any codegen or compile
146
+ errors. Do not commit the generated files automatically.
141
147
 
142
148
  ## Common failure modes
143
149
 
144
150
  | Symptom | Cause | Fix |
145
151
  |---|---|---|
146
- | `ECONNREFUSED 127.0.0.1:27017` | MongoDB not running | Ask the user to start Mongo; do not auto-start |
147
- | `EADDRINUSE :::4000` | Stale backend process | `pkill -f "example-backend.*src/index.ts"` then retry |
148
- | `Cannot find module '@rtk-query/codegen-openapi'` | Missing/stale deps | Re-run `bun run bootstrap` |
149
- | `openApiSdk.ts` has no endpoints | Backend wasn't ready when codegen ran | Step 4 wait was skipped — start over with a proper poll |
150
- | Codegen errors on `$ref` | Backend OpenAPI spec is malformed | Inspect `/openapi.json` manually; check recent changes to `openApi.ts` or model schemas |
152
+ | `ECONNREFUSED 127.0.0.1:27017` | MongoDB not running | Step 2 |
153
+ | Backend boots but change streams fail | Mongo is not a replica set | Start `mongod --replSet rs0` and initiate it |
154
+ | `EADDRINUSE :::4000` | Stale backend process | `pkill -f "example-backend.*src/index.ts"`, then retry |
155
+ | `fetch failed` / empty output | Backend not ready when codegen ran | Redo step 5 |
156
+ | Duplicate hook name error | Two operations map to one name | Add an `operationId` to one route |
157
+ | Screen imports a hook that no longer exists | Route renamed or excluded | Update the import; check `excludePaths` in `syncdb-codegen.json` |
151
158
 
152
159
  ## Notes
153
160
 
154
- - The codegen config lives at `example-frontend/openapi-config.ts`. The schema URL respects `OPENAPI_URL` if set, otherwise defaults to `http://localhost:4000/openapi.json`.
155
- - The script entry point is `example-frontend/scripts/generate-sdk.ts` — it shells out to the `@rtk-query/codegen-openapi` CLI, then post-processes the output with Biome.
156
- - Custom endpoints and cache tag types are layered on top in `example-frontend/store/sdk.ts` via `injectEndpoints`/`enhanceEndpoints`; that file is hand-maintained and is **not** overwritten by codegen.
161
+ - Script definitions: `example-frontend/package.json` (`api-sdk`, `sdk`, `sync-sdk`). They run
162
+ `syncdb/src/codegen/cli.ts` directly; consumer apps call the `terreno-syncdb-codegen` bin.
163
+ - Hook names match the old RTK codegen rule (`GET /auth/me` → `useGetAuthMeQuery`), so
164
+ migrated screens keep their names. Apps upgrading from `@terreno/rtk` follow
165
+ `docs/how-to/migrate-rtk-to-syncdb.md`.
@@ -41,10 +41,6 @@ modelRouter("/todos", Todo, {
41
41
  excludeFromCreate: ["ownerId"],
42
42
  excludeFromUpdate: ["ownerId"],
43
43
  },
44
- realtime: { // deprecated; removed in Terreno 58 — use `sync` instead
45
- methods: ["create", "update", "delete"],
46
- roomStrategy: "owner",
47
- },
48
44
  sync: {scope: {type: "owner"}},
49
45
  collectionActions: {
50
46
  bulkComplete: {