@contextual-io/cli 0.3.1 → 0.5.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 (40) hide show
  1. package/README.md +383 -44
  2. package/dist/base.d.ts +5 -1
  3. package/dist/base.js +19 -4
  4. package/dist/commands/config/add.js +2 -1
  5. package/dist/commands/config/current.js +2 -2
  6. package/dist/commands/config/delete.js +1 -1
  7. package/dist/commands/config/get.js +3 -4
  8. package/dist/commands/config/index.js +1 -1
  9. package/dist/commands/config/list.js +6 -6
  10. package/dist/commands/config/login.js +1 -1
  11. package/dist/commands/config/use.js +1 -1
  12. package/dist/commands/records/add.js +1 -1
  13. package/dist/commands/records/delete.js +2 -2
  14. package/dist/commands/records/get.js +2 -2
  15. package/dist/commands/records/index.js +1 -1
  16. package/dist/commands/records/list.d.ts +1 -0
  17. package/dist/commands/records/list.js +2 -1
  18. package/dist/commands/records/patch.d.ts +25 -0
  19. package/dist/commands/records/patch.js +106 -0
  20. package/dist/commands/records/query.js +1 -1
  21. package/dist/commands/records/replace.js +2 -2
  22. package/dist/commands/types/add.d.ts +11 -0
  23. package/dist/commands/types/add.js +57 -0
  24. package/dist/commands/types/delete.d.ts +12 -0
  25. package/dist/commands/types/delete.js +35 -0
  26. package/dist/commands/types/get.d.ts +12 -0
  27. package/dist/commands/types/get.js +33 -0
  28. package/dist/commands/types/index.d.ts +9 -0
  29. package/dist/commands/types/index.js +13 -0
  30. package/dist/commands/types/list.d.ts +20 -0
  31. package/dist/commands/types/list.js +129 -0
  32. package/dist/commands/types/replace.d.ts +13 -0
  33. package/dist/commands/types/replace.js +57 -0
  34. package/dist/models/uri.js +5 -0
  35. package/dist/models/user-config.d.ts +2 -2
  36. package/dist/models/user-config.js +2 -0
  37. package/dist/utils/endpoints.d.ts +1 -0
  38. package/dist/utils/endpoints.js +1 -0
  39. package/oclif.manifest.json +457 -20
  40. package/package.json +1 -1
@@ -0,0 +1,129 @@
1
+ /* eslint-disable unicorn/no-array-reduce */
2
+ import { Flags } from "@oclif/core";
3
+ import { BaseCommand } from "../../base.js";
4
+ import { getRegistryApiEndpoint } from "../../utils/endpoints.js";
5
+ import { endTime, startTime } from "../../utils/time.js";
6
+ const flagArrayToParam = (cmd, values) => values
7
+ .map(_ => _.split("="))
8
+ .map(([k, v]) => ({ [`${cmd}.${k}`]: v }))
9
+ .reduce((a, b) => ({ ...a, ...b }), {});
10
+ export default class TypesList extends BaseCommand {
11
+ static aliases = ["types search"];
12
+ static args = {};
13
+ static description = "List types.";
14
+ static examples = [
15
+ "<%= config.bin %> <%= command.id %>",
16
+ "<%= config.bin %> <%= command.id %> --order-by field1:desc",
17
+ "<%= config.bin %> <%= command.id %> -s field1=value1 -s field2=value2 --include-total",
18
+ ];
19
+ static flags = {
20
+ "exact-search": Flags.string({
21
+ char: "S",
22
+ description: "search fields exactly",
23
+ multiple: true,
24
+ }),
25
+ export: Flags.boolean({
26
+ allowNo: false,
27
+ char: "e",
28
+ description: "export data as JSONL",
29
+ }),
30
+ from: Flags.string({
31
+ char: "f",
32
+ description: "search from",
33
+ multiple: true,
34
+ }),
35
+ "include-total": Flags.boolean({
36
+ allowNo: false,
37
+ description: "include total count",
38
+ }),
39
+ "order-by": Flags.string({
40
+ char: "o",
41
+ description: "order fields",
42
+ multiple: true,
43
+ }),
44
+ "page-size": Flags.integer({
45
+ description: "number of types per page",
46
+ max: 250,
47
+ min: 0,
48
+ }),
49
+ "page-token": Flags.string({
50
+ description: "page token to fetch",
51
+ }),
52
+ progress: Flags.boolean({
53
+ allowNo: false,
54
+ char: "p",
55
+ description: "show progress during export",
56
+ }),
57
+ search: Flags.string({
58
+ char: "s",
59
+ description: "search fields",
60
+ multiple: true,
61
+ }),
62
+ to: Flags.string({
63
+ char: "t",
64
+ description: "search to",
65
+ multiple: true,
66
+ }),
67
+ };
68
+ async run() {
69
+ const params = {
70
+ ...(this.flags["page-size"] && { pageSize: this.flags["page-size"] }),
71
+ ...(this.flags["page-token"] && { pageToken: this.flags["page-token"] }),
72
+ ...(this.flags["include-total"] && { includeTotal: true }),
73
+ ...(this.flags["order-by"] && { orderBy: this.flags["order-by"] }),
74
+ ...flagArrayToParam("search", this.flags.search ?? []),
75
+ ...flagArrayToParam("exactSearch", this.flags["exact-search"] ?? []),
76
+ ...flagArrayToParam("from", this.flags.from ?? []),
77
+ ...flagArrayToParam("to", this.flags.to ?? []),
78
+ };
79
+ // eslint-disable-next-line unicorn/consistent-function-scoping
80
+ const list = (params) => this.fetch(({ silo, tenantId }) => getRegistryApiEndpoint(tenantId, silo) + "/api/v1/$page/types", {
81
+ body: JSON.stringify(params),
82
+ headers: {
83
+ "content-type": "application/json",
84
+ },
85
+ method: "POST",
86
+ });
87
+ if (this.flags.export) {
88
+ const pageSize = this.flags["page-size"] ?? 250;
89
+ params.pageSize = pageSize;
90
+ params.includeTotal = true;
91
+ const start = startTime();
92
+ let page = 1;
93
+ let nextPageToken;
94
+ let totalCount = 1;
95
+ do {
96
+ if (nextPageToken) {
97
+ params.pageToken = nextPageToken;
98
+ }
99
+ const xStart = startTime();
100
+ // eslint-disable-next-line no-await-in-loop
101
+ const x = await list(params);
102
+ const xDuration = endTime(xStart);
103
+ if (x.totalCount) {
104
+ totalCount = x.totalCount;
105
+ }
106
+ x.items
107
+ .map(_ => JSON.stringify(_))
108
+ // eslint-disable-next-line unicorn/no-array-for-each
109
+ .forEach(_ => this.log(_));
110
+ nextPageToken = x.nextPageToken;
111
+ if (this.flags.progress) {
112
+ const duration = endTime(start);
113
+ const totalPages = Math.ceil(totalCount / pageSize);
114
+ const recsPerSec = Math.ceil((page * pageSize) / (duration * 1000));
115
+ const eta = (((duration / page) * (totalPages - page)) / 1000).toFixed(2);
116
+ process.stderr.write(`\r\u001B[Kexporting types eta: ${eta}s totalCount: ${totalCount} page: ${page}/${totalPages} records/s: ${recsPerSec} lastCall: ${xDuration.toFixed(2)}ms`);
117
+ }
118
+ page++;
119
+ } while (nextPageToken);
120
+ if (this.flags.progress) {
121
+ process.stderr.write("\n");
122
+ }
123
+ }
124
+ else {
125
+ const rv = await list(params);
126
+ this.logJson(rv);
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from "../../base.js";
2
+ export default class TypesReplace extends BaseCommand<typeof TypesReplace> {
3
+ static args: {
4
+ uri: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ "input-file": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,57 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import fs, { createReadStream } from "node:fs";
3
+ import path from "node:path";
4
+ import { BaseCommand } from "../../base.js";
5
+ import { parseUri } from "../../models/uri.js";
6
+ import { getRegistryApiEndpoint } from "../../utils/endpoints.js";
7
+ import { readStreamToEnd } from "../../utils/stream.js";
8
+ export default class TypesReplace extends BaseCommand {
9
+ static args = {
10
+ "uri": Args.string({ description: "uri of type" }),
11
+ };
12
+ static description = "Replace a type.";
13
+ static examples = [
14
+ "<%= config.bin %> <%= command.id %> native-object:my-type --input-file record.json",
15
+ "<%= config.bin %> <%= command.id %> --type my-type --input-file record.json",
16
+ ];
17
+ static flags = {
18
+ "input-file": Flags.file({
19
+ char: "i",
20
+ default: "-",
21
+ description: "file to read. can read stdin if value is '-'",
22
+ }),
23
+ type: Flags.string({
24
+ char: "T",
25
+ description: "type",
26
+ }),
27
+ };
28
+ async run() {
29
+ const { uri } = this.args;
30
+ const { type: flagType } = this.flags;
31
+ let type = flagType;
32
+ if (uri) {
33
+ const { type: uriType } = parseUri(uri);
34
+ type = uriType;
35
+ }
36
+ if (!type) {
37
+ throw new Error("Type not provided");
38
+ }
39
+ const inputFile = this.flags["input-file"] === "-"
40
+ ? "-"
41
+ : path.resolve(this.flags["input-file"]);
42
+ if (inputFile && inputFile !== "-" && !fs.existsSync(inputFile)) {
43
+ throw new Error("File does not exist");
44
+ }
45
+ const input = (await readStreamToEnd(inputFile && inputFile !== "-"
46
+ ? createReadStream(inputFile)
47
+ : process.stdin)).join("");
48
+ const replace = (body) => this.fetch(({ silo, tenantId }) => getRegistryApiEndpoint(tenantId, silo) + `/api/v1/types/${type}`, {
49
+ body,
50
+ headers: {
51
+ "content-type": "application/json",
52
+ },
53
+ method: "PUT",
54
+ });
55
+ this.logJson(await replace(input));
56
+ }
57
+ }
@@ -1,6 +1,11 @@
1
1
  import { z } from "zod";
2
+ import { UrlSafeId } from "./url-safe-id.js";
2
3
  export const Uri = z.string().regex(/^native-object:[a-z0-9-]+(\/.+)?/, { error: "URI must match one of the following formats: 'native-object:<type-id>' or 'native-object:<type-id>/<instance-id>'" });
3
4
  export const parseUri = (uri) => {
5
+ const parsedType = UrlSafeId.safeParse(uri);
6
+ if (parsedType.success) {
7
+ return { type: parsedType.data };
8
+ }
4
9
  const parsed = Uri.parse(uri);
5
10
  const [type, id] = parsed.replace(/^native-object:/, "").split("/");
6
11
  return { id, type };
@@ -23,11 +23,11 @@ export declare const Config: z.ZodObject<{
23
23
  }, z.core.$strip>;
24
24
  export type Config = z.infer<typeof Config>;
25
25
  export declare const CleanConfig: z.ZodObject<{
26
- silo: z.ZodEnum<{
26
+ silo: z.ZodOptional<z.ZodEnum<{
27
27
  prod: "prod";
28
28
  qa: "qa";
29
29
  dev: "dev";
30
- }>;
30
+ }>>;
31
31
  tenantId: z.ZodString;
32
32
  configId: z.ZodString;
33
33
  }, z.core.$strip>;
@@ -15,6 +15,8 @@ export const Config = z.object({
15
15
  export const CleanConfig = Config.pick({
16
16
  silo: true,
17
17
  tenantId: true,
18
+ }).partial({
19
+ silo: true,
18
20
  }).extend({
19
21
  configId: z.string(),
20
22
  });
@@ -1,3 +1,4 @@
1
1
  import { Silo } from "../models/silo.js";
2
2
  export declare const getNativeObjectApiEndpoint: (tenantId: string, silo: Silo) => string;
3
+ export declare const getRegistryApiEndpoint: (tenantId: string, silo: Silo) => string;
3
4
  export declare const getAuthApiEndpoint: (tenantId: string, silo: Silo) => string;
@@ -1,2 +1,3 @@
1
1
  export const getNativeObjectApiEndpoint = (tenantId, silo) => `https://native-object.${tenantId}.my${silo === "prod" ? "" : `.${silo}`}.contextual.io`;
2
+ export const getRegistryApiEndpoint = (tenantId, silo) => `https://native-object-registry.${tenantId}.my${silo === "prod" ? "" : `.${silo}`}.contextual.io`;
2
3
  export const getAuthApiEndpoint = (tenantId, silo) => `https://auth.${tenantId}.my${silo === "prod" ? "" : `.${silo}`}.contextual.io`;