@seliseblocks/cli-os 0.2.8 → 0.2.10

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,5 +1,6 @@
1
1
  import { stringFlag } from "../../../lib/args.js";
2
2
  import { blocksRequest } from "../../../lib/api.js";
3
+ import { isRecord } from "../../../lib/data-response.js";
3
4
  import { writeOutput } from "../../../lib/output.js";
4
5
  import { requestContext } from "../../../lib/request-context.js";
5
6
  import { parseCommand, selectedProject } from "../../../lib/workspace.js";
@@ -14,4 +15,34 @@ export async function dataSchemaGet(argv) {
14
15
  query: { id }
15
16
  });
16
17
  writeOutput(result, flags);
18
+ // --json stays machine-readable and unchanged; human runs also get the
19
+ // exact GraphQL operation names, since they are naive string concatenation
20
+ // (no English pluralization) rather than the pattern users tend to guess.
21
+ if (!flags.json) {
22
+ for (const line of graphqlOperationLines(result))
23
+ console.log(line);
24
+ }
25
+ }
26
+ function graphqlOperationLines(result) {
27
+ const data = isRecord(result) && isRecord(result.data) ? result.data : undefined;
28
+ if (!data)
29
+ return [];
30
+ const schemaName = typeof data.schemaName === "string" ? data.schemaName : undefined;
31
+ const querySchema = typeof data.querySchema === "string" ? data.querySchema : undefined;
32
+ const mutationSchemas = Array.isArray(data.mutationSchemas)
33
+ ? data.mutationSchemas.filter((item) => typeof item === "string")
34
+ : [];
35
+ if (!schemaName && !querySchema && mutationSchemas.length === 0)
36
+ return [];
37
+ const lines = ["", "GraphQL operation names (exact -- resolver names are not English-pluralized):"];
38
+ if (querySchema)
39
+ lines.push(` Query: get${querySchema}`);
40
+ for (const mutation of mutationSchemas)
41
+ lines.push(` Mutation: ${mutation}`);
42
+ if (schemaName) {
43
+ lines.push(` Bulk insert: insertMany${schemaName}`);
44
+ lines.push(` Bulk update: updateMany${schemaName}`);
45
+ lines.push(` Bulk delete: deleteMany${schemaName}`);
46
+ }
47
+ return lines;
17
48
  }
@@ -1,15 +1,22 @@
1
+ import { integerFlag } from "../../../lib/args.js";
1
2
  import { blocksRequest } from "../../../lib/api.js";
3
+ import { unwrapSchemaListResponse } from "../../../lib/data-response.js";
2
4
  import { writeOutput } from "../../../lib/output.js";
3
5
  import { requestContext } from "../../../lib/request-context.js";
4
6
  import { parseCommand, selectedProject } from "../../../lib/workspace.js";
5
7
  export async function dataSchemaList(argv) {
6
8
  const { flags } = parseCommand(argv);
7
9
  const projectKey = await selectedProject(flags);
10
+ const pageNo = integerFlag(flags, "page", 1);
11
+ const pageSize = integerFlag(flags, "page-size", 100);
8
12
  const result = await blocksRequest("/data/v4/schemas", {
9
13
  impersonatedProjectAuth: true,
10
14
  ...requestContext(flags),
11
15
  projectTenantId: projectKey,
12
- query: { PageNo: 1, PageSize: 100, ProjectKey: projectKey }
16
+ query: { PageNo: pageNo, PageSize: pageSize, ProjectKey: projectKey }
13
17
  });
18
+ // Throws on a malformed envelope so an unexpected response shape is never
19
+ // mistaken for an empty schema list.
20
+ unwrapSchemaListResponse(result);
14
21
  writeOutput(result, flags);
15
22
  }
@@ -1,20 +1,33 @@
1
1
  import { blocksRequest } from "../../../lib/api.js";
2
2
  import { writeOutput } from "../../../lib/output.js";
3
3
  import { requestContext } from "../../../lib/request-context.js";
4
- import { writeSchemaFile } from "../../../lib/data-files.js";
4
+ import { toPortableSchema, writeSchemaFile } from "../../../lib/data-files.js";
5
+ import { unwrapSchemaListResponse } from "../../../lib/data-response.js";
5
6
  import { parseCommand, selectedProject } from "../../../lib/workspace.js";
7
+ const PAGE_SIZE = 500;
6
8
  export async function dataSchemaPull(argv) {
7
9
  const { flags } = parseCommand(argv);
8
10
  const projectKey = await selectedProject(flags);
9
- const result = await blocksRequest("/data/v4/schemas", {
10
- impersonatedProjectAuth: true,
11
- ...requestContext(flags),
12
- projectTenantId: projectKey,
13
- query: { PageNo: 1, PageSize: 500, ProjectKey: projectKey }
14
- });
11
+ const items = [];
12
+ let pageNo = 1;
13
+ let totalCount = Infinity;
14
+ while (items.length < totalCount) {
15
+ const response = await blocksRequest("/data/v4/schemas", {
16
+ impersonatedProjectAuth: true,
17
+ ...requestContext(flags),
18
+ projectTenantId: projectKey,
19
+ query: { PageNo: pageNo, PageSize: PAGE_SIZE, ProjectKey: projectKey }
20
+ });
21
+ const page = unwrapSchemaListResponse(response);
22
+ totalCount = page.totalCount;
23
+ if (page.items.length === 0)
24
+ break;
25
+ items.push(...page.items);
26
+ pageNo += 1;
27
+ }
15
28
  const files = [];
16
- for (const schema of result.data?.items ?? []) {
17
- files.push(await writeSchemaFile(schema));
29
+ for (const schema of items) {
30
+ files.push(await writeSchemaFile(toPortableSchema(schema)));
18
31
  }
19
32
  writeOutput({ files, count: files.length }, flags);
20
33
  }
@@ -2,6 +2,7 @@ import { booleanFlag } from "../../../lib/args.js";
2
2
  import { blocksRequest } from "../../../lib/api.js";
3
3
  import { confirmMutation } from "../../../lib/confirm.js";
4
4
  import { readSchemaFiles, validateSchemas } from "../../../lib/data-files.js";
5
+ import { isRecord, unwrapSchemaListResponse } from "../../../lib/data-response.js";
5
6
  import { writeOutput } from "../../../lib/output.js";
6
7
  import { requestContext } from "../../../lib/request-context.js";
7
8
  import { parseCommand, selectedProject } from "../../../lib/workspace.js";
@@ -18,22 +19,43 @@ export async function dataSchemaPush(argv) {
18
19
  }
19
20
  await confirmMutation(flags, `Push ${schemas.length} data schema file(s) to project '${projectKey}'.`);
20
21
  const results = [];
21
- for (const { schema } of schemas) {
22
- const existing = await blocksRequest("/data/v4/schemas", {
22
+ const warnings = [];
23
+ for (const { file, schema } of schemas) {
24
+ const schemaName = String(schema.schemaName);
25
+ const localId = schema.itemId ?? schema.id;
26
+ // Look up the destination project's own copy of this schema by name -- a
27
+ // local id/itemId may belong to a different project and must never be
28
+ // trusted directly (see CLAUDE_HANDOFF.md #1).
29
+ const existingResponse = await blocksRequest("/data/v4/schemas", {
23
30
  impersonatedProjectAuth: true,
24
31
  ...requestContext(flags),
25
32
  projectTenantId: projectKey,
26
- query: { PageNo: 1, PageSize: 5, ProjectKey: projectKey, SchemaName: String(schema.schemaName) }
33
+ query: { PageNo: 1, PageSize: 5, ProjectKey: projectKey, SchemaName: schemaName }
27
34
  });
28
- const itemId = schema.itemId ?? existing.data?.items?.[0]?.itemId;
29
- const body = { ...schema, itemId, projectKey };
30
- results.push(await blocksRequest("/data/v4/schemas/define", {
35
+ const { items } = unwrapSchemaListResponse(existingResponse);
36
+ const destination = items.find((item) => item.schemaName === schemaName);
37
+ const destinationId = typeof destination?.id === "string" ? destination.id : undefined;
38
+ if (!destinationId && localId) {
39
+ warnings.push(`${file}: ignoring local id for '${schemaName}' -- no matching schema found in project '${projectKey}'; creating instead.`);
40
+ }
41
+ const portable = { ...schema };
42
+ delete portable.itemId;
43
+ delete portable.id;
44
+ const body = destinationId ? { ...portable, itemId: destinationId, projectKey } : { ...portable, projectKey };
45
+ const response = await blocksRequest("/data/v4/schemas/define", {
31
46
  body,
32
47
  impersonatedProjectAuth: true,
33
48
  ...requestContext(flags),
34
49
  projectTenantId: projectKey,
35
- method: itemId ? "PUT" : "POST"
36
- }));
50
+ method: destinationId ? "PUT" : "POST"
51
+ });
52
+ if (response === undefined || response === null) {
53
+ throw new Error(`Push failed for schema '${schemaName}': the server returned an empty response.`);
54
+ }
55
+ if (isRecord(response) && response.isSuccess === false) {
56
+ throw new Error(`Push failed for schema '${schemaName}': ${JSON.stringify(response)}`);
57
+ }
58
+ results.push(response);
37
59
  }
38
- writeOutput({ results }, flags);
60
+ writeOutput({ results, ...(warnings.length ? { warnings } : {}) }, flags);
39
61
  }