@seliseblocks/cli-os 0.2.2 → 0.2.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.
Files changed (37) hide show
  1. package/AI_USAGE_GUIDE.md +551 -546
  2. package/LICENSE +21 -21
  3. package/README.md +171 -171
  4. package/bin/run.js +2 -2
  5. package/dist/commands/data/files/delete.js +5 -4
  6. package/dist/commands/data/files/get-many.js +1 -1
  7. package/dist/commands/data/files/get.js +1 -1
  8. package/dist/commands/data/files/info.js +1 -1
  9. package/dist/commands/data/files/object-tree.d.ts +23 -0
  10. package/dist/commands/data/files/object-tree.js +238 -0
  11. package/dist/commands/data/files/presigned-upload-url.js +9 -2
  12. package/dist/commands/data/files/update-additional-info.js +2 -2
  13. package/dist/commands/data/files/upload-to-local-storage.js +2 -2
  14. package/dist/commands/data/files/upload.d.ts +2 -4
  15. package/dist/commands/data/files/upload.js +14 -28
  16. package/dist/index.js +685 -647
  17. package/dist/skills/blocks-data-gateway-configuration/SKILL.md +204 -204
  18. package/dist/skills/blocks-data-gateway-crud/SKILL.md +223 -223
  19. package/dist/skills/blocks-data-storage/SKILL.md +253 -161
  20. package/dist/skills/blocks-data-storage/flows/object-management.md +124 -0
  21. package/dist/skills/blocks-frontend-local-https/SKILL.md +100 -100
  22. package/dist/skills/blocks-iam-account/SKILL.md +169 -169
  23. package/dist/skills/blocks-iam-sso-oidc-implementation/SKILL.md +80 -80
  24. package/dist/skills/blocks-iam-users/SKILL.md +131 -131
  25. package/dist/skills/blocks-localization-configuration/SKILL.md +149 -149
  26. package/dist/skills/blocks-localization-implementation/SKILL.md +63 -63
  27. package/dist/skills/blocks-onboarding/SKILL.md +77 -77
  28. package/dist/skills/blocks-storage-configuration/SKILL.md +4 -4
  29. package/package.json +47 -47
  30. package/dist/commands/data/files/create-folder.d.ts +0 -1
  31. package/dist/commands/data/files/create-folder.js +0 -34
  32. package/dist/commands/data/files/delete-folder.d.ts +0 -1
  33. package/dist/commands/data/files/delete-folder.js +0 -25
  34. package/dist/commands/data/files/dms-list.d.ts +0 -1
  35. package/dist/commands/data/files/dms-list.js +0 -27
  36. package/dist/commands/data/files/dms-upload.d.ts +0 -6
  37. package/dist/commands/data/files/dms-upload.js +0 -41
@@ -1,223 +1,223 @@
1
- ---
2
- name: blocks-data-gateway-crud
3
- description: "Implement create/read/update/delete against a SELISE Blocks project's runtime Data Gateway using the @seliseblocks/client SDK. Use data.collection(schemaName) for straightforward per-item CRUD, data.graphql() for joins or custom query shapes, and data.schemas.*/data.validations.* for schema/validation metadata. Shows how to wire CRUD into the React 18 + Vite + TanStack Query app that blocks new web scaffolds. Use whenever the user wants to read or write actual records through a Blocks Data schema from app code."
4
- ---
5
-
6
- # Blocks Data - Gateway CRUD
7
-
8
- Once a schema exists and has been reloaded via the blocks-data-gateway-configuration skill, the Data Gateway exposes runtime records through GraphQL. This skill shows how to use the generated app's shared `@seliseblocks/client` instance for CRUD. Do not use raw `fetch` or `curl` against Blocks APIs from app code.
9
-
10
- Prerequisite: a project selected via `blocks use` and an app scaffolded with `blocks new web <name> ...`. If either is missing, run the blocks-onboarding skill first.
11
-
12
- ## Use the Existing Client
13
-
14
- The scaffold creates one `createBlocksClient()` call in `src/lib/blocks/client.ts`:
15
-
16
- ```ts
17
- import { createBlocksClient } from "@seliseblocks/client";
18
- import { blocksConfig } from "./config";
19
- import { getValidAccessToken } from "./auth";
20
-
21
- export const blocksClient = createBlocksClient({
22
- accessToken: () => getValidAccessToken(),
23
- apiUrl: blocksConfig.apiUrl,
24
- appDomain: blocksConfig.appDomain,
25
- oidc: { clientId: blocksConfig.oidcClientId, scope: blocksConfig.oidcScope, url: blocksConfig.oidcUrl },
26
- xBlocksKey: blocksConfig.xBlocksKey
27
- });
28
- ```
29
-
30
- Import that existing instance from feature code. Do not create a second client.
31
-
32
- - `xBlocksKey` is the project's public tenant key from `VITE_BLOCKS_X_BLOCKS_KEY`.
33
- - The SDK does not add `ProjectKey` to runtime Data calls.
34
- - The HTTP client sends `credentials: "include"` so hosted-login session cookies are included.
35
- - Never use the CLI's impersonation token or project token in browser code.
36
-
37
- ## Preferred CRUD Helper
38
-
39
- Use `blocksClient.data.collection<T>(schemaName, options)` for normal CRUD. Pass the schema name, not the collection name. For a schema named `Product` with collection `Products`, pass `"Product"`.
40
-
41
- ```ts
42
- const products = blocksClient.data.collection<Product>("Product", {
43
- fields: ["name", "price", "status"]
44
- });
45
-
46
- products.list({ pageNo: 1, pageSize: 20 });
47
- products.get(itemId);
48
- products.create(payload);
49
- products.update(itemId, payload);
50
- products.delete(itemId);
51
- ```
52
-
53
- What the helper does:
54
-
55
- - `list` and `get` call `query getProducts($input: DynamicQueryInput)`.
56
- - `create` calls `mutation insertProduct($input: ProductInsertInput!)`.
57
- - `update` calls `mutation updateProduct($filter: String, $input: ProductUpdateInput!)`.
58
- - `delete` calls `mutation deleteProduct($filter: String, $input: ProductDeleteInput!)`.
59
- - `get`, `update`, and `delete` address records by `ItemId`.
60
- - `fields` controls the GraphQL selection for returned items; `ItemId` is always selected.
61
-
62
- `list` accepts paging plus optional GraphQL dynamic input values:
63
-
64
- ```ts
65
- await products.list({
66
- pageNo: 1,
67
- pageSize: 20,
68
- filter: { status: "Active" },
69
- sort: { name: 1 }
70
- });
71
- ```
72
-
73
- For search, only send filters that the schema/runtime actually supports. If unsure, start with simple paging and client-side filtering, or inspect the schema first.
74
-
75
- ## Product Example
76
-
77
- ```ts
78
- // src/features/products/productsApi.ts
79
- import { blocksClient } from "../../lib/blocks/client";
80
-
81
- export type Product = Record<string, unknown> & {
82
- itemId?: string;
83
- id?: string;
84
- name?: string;
85
- price?: number;
86
- status?: string;
87
- };
88
-
89
- export type ProductInput = { name: string; price: number; status: string };
90
-
91
- const products = blocksClient.data.collection<Product>("Product", {
92
- fields: ["name", "price", "status"]
93
- });
94
-
95
- export async function listProducts({ page, pageSize }: { page: number; pageSize: number }) {
96
- const response = await products.list({ pageNo: page, pageSize });
97
- return normalizeProductList(response);
98
- }
99
-
100
- export function createProduct(input: ProductInput) {
101
- return products.create(input);
102
- }
103
-
104
- export function updateProduct(product: Product) {
105
- return products.update(productId(product), product);
106
- }
107
-
108
- export function deleteProduct(product: Product) {
109
- return products.delete(productId(product));
110
- }
111
-
112
- function productId(product: Product): string {
113
- const id = product.itemId ?? product.id;
114
- if (!id) throw new Error("Product item id is missing.");
115
- return String(id);
116
- }
117
-
118
- function normalizeProductList(response: unknown): { items: Product[]; totalCount: number } {
119
- const record = response as {
120
- data?: {
121
- getProducts?: { items?: Product[]; totalCount?: number };
122
- items?: Product[];
123
- totalCount?: number;
124
- };
125
- items?: Product[];
126
- totalCount?: number;
127
- };
128
- const gateway = record.data?.getProducts;
129
- const items = gateway?.items ?? record.data?.items ?? record.items ?? [];
130
- return { items, totalCount: gateway?.totalCount ?? record.data?.totalCount ?? record.totalCount ?? items.length };
131
- }
132
- ```
133
-
134
- ```ts
135
- // src/features/products/useProducts.ts
136
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
137
- import { createProduct, deleteProduct, listProducts, updateProduct } from "./productsApi";
138
- import type { Product, ProductInput } from "./productsApi";
139
-
140
- export function useProductsQuery(page: number, pageSize: number) {
141
- return useQuery({
142
- queryKey: ["products", page, pageSize],
143
- queryFn: () => listProducts({ page, pageSize })
144
- });
145
- }
146
-
147
- export function useProductMutations() {
148
- const queryClient = useQueryClient();
149
- const invalidate = () => queryClient.invalidateQueries({ queryKey: ["products"] });
150
-
151
- return {
152
- create: useMutation({ mutationFn: (input: ProductInput) => createProduct(input), onSuccess: invalidate }),
153
- update: useMutation({ mutationFn: (product: Product) => updateProduct(product), onSuccess: invalidate }),
154
- remove: useMutation({ mutationFn: (product: Product) => deleteProduct(product), onSuccess: invalidate })
155
- };
156
- }
157
- ```
158
-
159
- ## When to Use `data.graphql()`
160
-
161
- Use `data.graphql({ query, variables, operationName })` when the collection helper is too small:
162
-
163
- - joins across schemas;
164
- - custom nested selections;
165
- - generated `where`, `order`, or `paging` arguments;
166
- - bulk operations such as `insertManyProduct`.
167
-
168
- Example:
169
-
170
- ```ts
171
- const result = await blocksClient.data.graphql({
172
- operationName: "getProducts",
173
- query: `
174
- query getProducts($input: DynamicQueryInput) {
175
- getProducts(input: $input) {
176
- items { ItemId name price status }
177
- totalCount
178
- }
179
- }
180
- `,
181
- variables: { input: { pageNo: 1, pageSize: 20 } }
182
- });
183
- ```
184
-
185
- ## Schema Metadata
186
-
187
- Use `data.schemas.*` when building dynamic UI:
188
-
189
- - `data.schemas.list(options)` or `data.schemas.get(schemaName)` for discovery;
190
- - `data.schemas.infoByName(schemaName)` for field-level metadata;
191
- - `data.schemas.info()` for general schema info;
192
- - `data.schemas.aggregation(options)` for summary views;
193
- - `data.schemas.getById(id)` when you already have the schema id.
194
-
195
- Use `data.validations.*` to read field-level validation rules at runtime (e.g. to drive client-side form validation from the same rules the backend enforces):
196
-
197
- - `data.validations.list(options)` for all rules, with optional schema/field/paging filters;
198
- - `data.validations.bySchemaId(schemaId)` for every rule on one schema;
199
- - `data.validations.bySchemaAndField({ schemaId, fieldName })` for one field's rule;
200
- - `data.validations.getById(id)` when you already have the validation id.
201
-
202
- Validation rules are authored and saved separately via `blocks data validation save` (see the blocks-data-gateway-configuration skill); this SDK namespace only reads them.
203
-
204
- ## Gotchas
205
-
206
- - Pass schema name, not collection name: `Product`, not `Products`.
207
- - Generated query names pluralize by appending `s`: `getProducts`.
208
- - Generated mutation names stay singular: `insertProduct`, `updateProduct`, `deleteProduct`.
209
- - Dynamic item selections use schema field names such as `ItemId`, `name`, `price`.
210
- - Mutation response fields are lower camel case: `acknowledged`, `itemId`, `message`, `totalImpactedData`.
211
- - If GraphQL says the field does not exist, the schema probably has not been created or reloaded.
212
- - Keep one `blocksClient` per app.
213
-
214
- ## Prompt Routing
215
-
216
- ```text
217
- Wire create/read/update/delete for Product into my React app -> this skill
218
- Build a paged table of Orders in the dashboard -> this skill
219
- I need a GraphQL query that joins Orders with Customer details -> this skill, data.graphql
220
- Build a dynamic form from my schema's field metadata -> this skill, data.schemas.infoByName
221
- Create a Product schema with title/price and reload it -> blocks-data-gateway-configuration
222
- Upload a PDF and get a download link -> blocks-data-storage
223
- ```
1
+ ---
2
+ name: blocks-data-gateway-crud
3
+ description: "Implement create/read/update/delete against a SELISE Blocks project's runtime Data Gateway using the @seliseblocks/client SDK. Use data.collection(schemaName) for straightforward per-item CRUD, data.graphql() for joins or custom query shapes, and data.schemas.*/data.validations.* for schema/validation metadata. Shows how to wire CRUD into the React 18 + Vite + TanStack Query app that blocks new web scaffolds. Use whenever the user wants to read or write actual records through a Blocks Data schema from app code."
4
+ ---
5
+
6
+ # Blocks Data - Gateway CRUD
7
+
8
+ Once a schema exists and has been reloaded via the blocks-data-gateway-configuration skill, the Data Gateway exposes runtime records through GraphQL. This skill shows how to use the generated app's shared `@seliseblocks/client` instance for CRUD. Do not use raw `fetch` or `curl` against Blocks APIs from app code.
9
+
10
+ Prerequisite: a project selected via `blocks use` and an app scaffolded with `blocks new web <name> ...`. If either is missing, run the blocks-onboarding skill first.
11
+
12
+ ## Use the Existing Client
13
+
14
+ The scaffold creates one `createBlocksClient()` call in `src/lib/blocks/client.ts`:
15
+
16
+ ```ts
17
+ import { createBlocksClient } from "@seliseblocks/client";
18
+ import { blocksConfig } from "./config";
19
+ import { getValidAccessToken } from "./auth";
20
+
21
+ export const blocksClient = createBlocksClient({
22
+ accessToken: () => getValidAccessToken(),
23
+ apiUrl: blocksConfig.apiUrl,
24
+ appDomain: blocksConfig.appDomain,
25
+ oidc: { clientId: blocksConfig.oidcClientId, scope: blocksConfig.oidcScope, url: blocksConfig.oidcUrl },
26
+ xBlocksKey: blocksConfig.xBlocksKey
27
+ });
28
+ ```
29
+
30
+ Import that existing instance from feature code. Do not create a second client.
31
+
32
+ - `xBlocksKey` is the project's public tenant key from `VITE_BLOCKS_X_BLOCKS_KEY`.
33
+ - The SDK does not add `ProjectKey` to runtime Data calls.
34
+ - The HTTP client sends `credentials: "include"` so hosted-login session cookies are included.
35
+ - Never use the CLI's impersonation token or project token in browser code.
36
+
37
+ ## Preferred CRUD Helper
38
+
39
+ Use `blocksClient.data.collection<T>(schemaName, options)` for normal CRUD. Pass the schema name, not the collection name. For a schema named `Product` with collection `Products`, pass `"Product"`.
40
+
41
+ ```ts
42
+ const products = blocksClient.data.collection<Product>("Product", {
43
+ fields: ["name", "price", "status"]
44
+ });
45
+
46
+ products.list({ pageNo: 1, pageSize: 20 });
47
+ products.get(itemId);
48
+ products.create(payload);
49
+ products.update(itemId, payload);
50
+ products.delete(itemId);
51
+ ```
52
+
53
+ What the helper does:
54
+
55
+ - `list` and `get` call `query getProducts($input: DynamicQueryInput)`.
56
+ - `create` calls `mutation insertProduct($input: ProductInsertInput!)`.
57
+ - `update` calls `mutation updateProduct($filter: String, $input: ProductUpdateInput!)`.
58
+ - `delete` calls `mutation deleteProduct($filter: String, $input: ProductDeleteInput!)`.
59
+ - `get`, `update`, and `delete` address records by `ItemId`.
60
+ - `fields` controls the GraphQL selection for returned items; `ItemId` is always selected.
61
+
62
+ `list` accepts paging plus optional GraphQL dynamic input values:
63
+
64
+ ```ts
65
+ await products.list({
66
+ pageNo: 1,
67
+ pageSize: 20,
68
+ filter: { status: "Active" },
69
+ sort: { name: 1 }
70
+ });
71
+ ```
72
+
73
+ For search, only send filters that the schema/runtime actually supports. If unsure, start with simple paging and client-side filtering, or inspect the schema first.
74
+
75
+ ## Product Example
76
+
77
+ ```ts
78
+ // src/features/products/productsApi.ts
79
+ import { blocksClient } from "../../lib/blocks/client";
80
+
81
+ export type Product = Record<string, unknown> & {
82
+ itemId?: string;
83
+ id?: string;
84
+ name?: string;
85
+ price?: number;
86
+ status?: string;
87
+ };
88
+
89
+ export type ProductInput = { name: string; price: number; status: string };
90
+
91
+ const products = blocksClient.data.collection<Product>("Product", {
92
+ fields: ["name", "price", "status"]
93
+ });
94
+
95
+ export async function listProducts({ page, pageSize }: { page: number; pageSize: number }) {
96
+ const response = await products.list({ pageNo: page, pageSize });
97
+ return normalizeProductList(response);
98
+ }
99
+
100
+ export function createProduct(input: ProductInput) {
101
+ return products.create(input);
102
+ }
103
+
104
+ export function updateProduct(product: Product) {
105
+ return products.update(productId(product), product);
106
+ }
107
+
108
+ export function deleteProduct(product: Product) {
109
+ return products.delete(productId(product));
110
+ }
111
+
112
+ function productId(product: Product): string {
113
+ const id = product.itemId ?? product.id;
114
+ if (!id) throw new Error("Product item id is missing.");
115
+ return String(id);
116
+ }
117
+
118
+ function normalizeProductList(response: unknown): { items: Product[]; totalCount: number } {
119
+ const record = response as {
120
+ data?: {
121
+ getProducts?: { items?: Product[]; totalCount?: number };
122
+ items?: Product[];
123
+ totalCount?: number;
124
+ };
125
+ items?: Product[];
126
+ totalCount?: number;
127
+ };
128
+ const gateway = record.data?.getProducts;
129
+ const items = gateway?.items ?? record.data?.items ?? record.items ?? [];
130
+ return { items, totalCount: gateway?.totalCount ?? record.data?.totalCount ?? record.totalCount ?? items.length };
131
+ }
132
+ ```
133
+
134
+ ```ts
135
+ // src/features/products/useProducts.ts
136
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
137
+ import { createProduct, deleteProduct, listProducts, updateProduct } from "./productsApi";
138
+ import type { Product, ProductInput } from "./productsApi";
139
+
140
+ export function useProductsQuery(page: number, pageSize: number) {
141
+ return useQuery({
142
+ queryKey: ["products", page, pageSize],
143
+ queryFn: () => listProducts({ page, pageSize })
144
+ });
145
+ }
146
+
147
+ export function useProductMutations() {
148
+ const queryClient = useQueryClient();
149
+ const invalidate = () => queryClient.invalidateQueries({ queryKey: ["products"] });
150
+
151
+ return {
152
+ create: useMutation({ mutationFn: (input: ProductInput) => createProduct(input), onSuccess: invalidate }),
153
+ update: useMutation({ mutationFn: (product: Product) => updateProduct(product), onSuccess: invalidate }),
154
+ remove: useMutation({ mutationFn: (product: Product) => deleteProduct(product), onSuccess: invalidate })
155
+ };
156
+ }
157
+ ```
158
+
159
+ ## When to Use `data.graphql()`
160
+
161
+ Use `data.graphql({ query, variables, operationName })` when the collection helper is too small:
162
+
163
+ - joins across schemas;
164
+ - custom nested selections;
165
+ - generated `where`, `order`, or `paging` arguments;
166
+ - bulk operations such as `insertManyProduct`.
167
+
168
+ Example:
169
+
170
+ ```ts
171
+ const result = await blocksClient.data.graphql({
172
+ operationName: "getProducts",
173
+ query: `
174
+ query getProducts($input: DynamicQueryInput) {
175
+ getProducts(input: $input) {
176
+ items { ItemId name price status }
177
+ totalCount
178
+ }
179
+ }
180
+ `,
181
+ variables: { input: { pageNo: 1, pageSize: 20 } }
182
+ });
183
+ ```
184
+
185
+ ## Schema Metadata
186
+
187
+ Use `data.schemas.*` when building dynamic UI:
188
+
189
+ - `data.schemas.list(options)` or `data.schemas.get(schemaName)` for discovery;
190
+ - `data.schemas.infoByName(schemaName)` for field-level metadata;
191
+ - `data.schemas.info()` for general schema info;
192
+ - `data.schemas.aggregation(options)` for summary views;
193
+ - `data.schemas.getById(id)` when you already have the schema id.
194
+
195
+ Use `data.validations.*` to read field-level validation rules at runtime (e.g. to drive client-side form validation from the same rules the backend enforces):
196
+
197
+ - `data.validations.list(options)` for all rules, with optional schema/field/paging filters;
198
+ - `data.validations.bySchemaId(schemaId)` for every rule on one schema;
199
+ - `data.validations.bySchemaAndField({ schemaId, fieldName })` for one field's rule;
200
+ - `data.validations.getById(id)` when you already have the validation id.
201
+
202
+ Validation rules are authored and saved separately via `blocks data validation save` (see the blocks-data-gateway-configuration skill); this SDK namespace only reads them.
203
+
204
+ ## Gotchas
205
+
206
+ - Pass schema name, not collection name: `Product`, not `Products`.
207
+ - Generated query names pluralize by appending `s`: `getProducts`.
208
+ - Generated mutation names stay singular: `insertProduct`, `updateProduct`, `deleteProduct`.
209
+ - Dynamic item selections use schema field names such as `ItemId`, `name`, `price`.
210
+ - Mutation response fields are lower camel case: `acknowledged`, `itemId`, `message`, `totalImpactedData`.
211
+ - If GraphQL says the field does not exist, the schema probably has not been created or reloaded.
212
+ - Keep one `blocksClient` per app.
213
+
214
+ ## Prompt Routing
215
+
216
+ ```text
217
+ Wire create/read/update/delete for Product into my React app -> this skill
218
+ Build a paged table of Orders in the dashboard -> this skill
219
+ I need a GraphQL query that joins Orders with Customer details -> this skill, data.graphql
220
+ Build a dynamic form from my schema's field metadata -> this skill, data.schemas.infoByName
221
+ Create a Product schema with title/price and reload it -> blocks-data-gateway-configuration
222
+ Upload a PDF and get a download link -> blocks-data-storage
223
+ ```