@prismatic-io/prism 4.1.1 → 4.2.2

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.
@@ -8,7 +8,10 @@ class ListCommand extends core_1.Command {
8
8
  const result = await (0, graphql_1.gqlRequest)({
9
9
  document: (0, graphql_1.gql) `
10
10
  query listAlertEvents($alertMonitorId: ID) {
11
- alertEvents(monitor: $alertMonitorId) {
11
+ alertEvents(
12
+ monitor: $alertMonitorId
13
+ sortBy: [{ field: CREATED_AT, direction: DESC }]
14
+ ) {
12
15
  nodes {
13
16
  id
14
17
  monitor {
@@ -5,19 +5,32 @@ const graphql_1 = require("../../../graphql");
5
5
  class ListCommand extends core_1.Command {
6
6
  async run() {
7
7
  const { flags } = await this.parse(ListCommand);
8
- const result = await (0, graphql_1.gqlRequest)({
9
- document: (0, graphql_1.gql) `
10
- query listAlertGroups {
11
- alertGroups {
12
- nodes {
13
- id
14
- name
8
+ let alertGroups = [];
9
+ let hasNextPage = true;
10
+ let cursor = "";
11
+ while (hasNextPage) {
12
+ const { alertGroups: { nodes, pageInfo }, } = await (0, graphql_1.gqlRequest)({
13
+ document: (0, graphql_1.gql) `
14
+ query listAlertGroups($after: String) {
15
+ alertGroups(after: $after) {
16
+ nodes {
17
+ id
18
+ name
19
+ }
20
+ pageInfo {
21
+ hasNextPage
22
+ endCursor
23
+ }
15
24
  }
16
25
  }
26
+ `,
27
+ variables: { after: cursor },
28
+ });
29
+ alertGroups = [...alertGroups, ...nodes];
30
+ cursor = pageInfo.endCursor;
31
+ hasNextPage = pageInfo.hasNextPage;
17
32
  }
18
- `,
19
- });
20
- core_1.CliUx.ux.table(result.alertGroups.nodes, {
33
+ core_1.CliUx.ux.table(alertGroups, {
21
34
  id: {
22
35
  minWidth: 8,
23
36
  extended: true,
@@ -5,26 +5,56 @@ const graphql_1 = require("../../../graphql");
5
5
  class ListCommand extends core_1.Command {
6
6
  async run() {
7
7
  const { flags } = await this.parse(ListCommand);
8
- const result = await (0, graphql_1.gqlRequest)({
9
- document: (0, graphql_1.gql) `
10
- query listAlertMonitors {
11
- alertMonitors {
12
- nodes {
13
- id
14
- name
15
- triggered
8
+ let alertMonitors = [];
9
+ let hasNextPage = true;
10
+ let cursor = "";
11
+ while (hasNextPage) {
12
+ const { alertMonitors: { nodes, pageInfo }, } = await (0, graphql_1.gqlRequest)({
13
+ document: (0, graphql_1.gql) `
14
+ query listAlertMonitors($after: String) {
15
+ alertMonitors(after: $after) {
16
+ nodes {
17
+ id
18
+ name
19
+ triggered
20
+ instance {
21
+ id
22
+ name
23
+ customer {
24
+ id
25
+ name
26
+ }
27
+ }
28
+ }
29
+ pageInfo {
30
+ hasNextPage
31
+ endCursor
32
+ }
16
33
  }
17
34
  }
35
+ `,
36
+ variables: { after: cursor },
37
+ });
38
+ alertMonitors = [...alertMonitors, ...nodes];
39
+ cursor = pageInfo.endCursor;
40
+ hasNextPage = pageInfo.hasNextPage;
18
41
  }
19
- `,
20
- });
21
- core_1.CliUx.ux.table(result.alertMonitors.nodes, {
42
+ core_1.CliUx.ux.table(alertMonitors, {
22
43
  id: {
23
44
  minWidth: 8,
24
45
  extended: true,
25
46
  },
26
47
  name: {},
27
48
  triggered: {},
49
+ customer: {
50
+ get: ({ instance: { customer } }) => customer.name,
51
+ },
52
+ customerId: {
53
+ extended: true,
54
+ get: ({ instance: { customer } }) => customer.id,
55
+ },
56
+ instance: { get: ({ instance }) => instance.name },
57
+ instanceId: { extended: true, get: ({ instance }) => instance.id },
28
58
  }, { ...flags });
29
59
  }
30
60
  }
@@ -5,22 +5,35 @@ const graphql_1 = require("../../../graphql");
5
5
  class ListCommand extends core_1.Command {
6
6
  async run() {
7
7
  const { flags } = await this.parse(ListCommand);
8
- const result = await (0, graphql_1.gqlRequest)({
9
- document: (0, graphql_1.gql) `
10
- query listAlertWebhooks {
11
- alertWebhooks {
12
- nodes {
13
- id
14
- name
15
- payloadTemplate
16
- url
17
- headers
8
+ let alertWebhooks = [];
9
+ let hasNextPage = true;
10
+ let cursor = "";
11
+ while (hasNextPage) {
12
+ const { alertWebhooks: { nodes, pageInfo }, } = await (0, graphql_1.gqlRequest)({
13
+ document: (0, graphql_1.gql) `
14
+ query listAlertWebhooks($after: String) {
15
+ alertWebhooks(after: $after) {
16
+ nodes {
17
+ id
18
+ name
19
+ payloadTemplate
20
+ url
21
+ headers
22
+ }
23
+ pageInfo {
24
+ hasNextPage
25
+ endCursor
26
+ }
18
27
  }
19
28
  }
29
+ `,
30
+ variables: { after: cursor },
31
+ });
32
+ alertWebhooks = [...alertWebhooks, ...nodes];
33
+ cursor = pageInfo.endCursor;
34
+ hasNextPage = pageInfo.hasNextPage;
20
35
  }
21
- `,
22
- });
23
- core_1.CliUx.ux.table(result.alertWebhooks.nodes, {
36
+ core_1.CliUx.ux.table(alertWebhooks, {
24
37
  id: {
25
38
  minWidth: 8,
26
39
  extended: true,
@@ -4,30 +4,54 @@ const core_1 = require("@oclif/core");
4
4
  const graphql_1 = require("../../../graphql");
5
5
  class ListCommand extends core_1.Command {
6
6
  async run() {
7
- const { flags } = await this.parse(ListCommand);
8
- const result = await (0, graphql_1.gqlRequest)({
9
- document: (0, graphql_1.gql) `
10
- query listComponentActions {
11
- components {
12
- nodes {
13
- actions(isTrigger: false) {
14
- nodes {
15
- id
16
- key
17
- label
18
- description
19
- component {
7
+ const { flags, args: { "Component Key": componentKey }, } = await this.parse(ListCommand);
8
+ let actions = [];
9
+ let componentId;
10
+ let hasNextPage = true;
11
+ let cursor = "";
12
+ while (hasNextPage) {
13
+ const { components: { nodes: [component], }, } = await (0, graphql_1.gqlRequest)({
14
+ document: (0, graphql_1.gql) `
15
+ query listComponentActions(
16
+ $componentKey: String
17
+ $after: String
18
+ $public: Boolean
19
+ ) {
20
+ components(key: $componentKey, public: $public) {
21
+ nodes {
22
+ id
23
+ key
24
+ actions(isTrigger: false, after: $after) {
25
+ nodes {
20
26
  id
21
27
  key
28
+ label
29
+ description
30
+ }
31
+ pageInfo {
32
+ hasNextPage
33
+ endCursor
22
34
  }
23
35
  }
24
36
  }
25
37
  }
26
38
  }
39
+ `,
40
+ variables: {
41
+ after: cursor,
42
+ componentKey,
43
+ public: flags.public ? true : flags.private ? false : null,
44
+ },
45
+ });
46
+ if (!component) {
47
+ console.log("The key you provided is not valid. Please run 'prism components:list -x' and identify a valid component key.");
48
+ this.exit(1);
49
+ }
50
+ actions = [...actions, ...component.actions.nodes];
51
+ componentId = component.id;
52
+ cursor = component.actions.pageInfo.endCursor;
53
+ hasNextPage = component.actions.hasNextPage;
27
54
  }
28
- `,
29
- });
30
- const actions = result.components.nodes.flatMap(({ actions }) => actions.nodes);
31
55
  core_1.CliUx.ux.table(actions, {
32
56
  id: {
33
57
  minWidth: 8,
@@ -40,11 +64,11 @@ class ListCommand extends core_1.Command {
40
64
  label: {},
41
65
  description: {},
42
66
  componentid: {
43
- get: ({ component: { id } }) => id,
67
+ get: () => componentId,
44
68
  extended: true,
45
69
  },
46
70
  componentkey: {
47
- get: ({ component: { key } }) => key,
71
+ get: () => componentKey,
48
72
  extended: true,
49
73
  },
50
74
  }, { ...flags });
@@ -52,4 +76,21 @@ class ListCommand extends core_1.Command {
52
76
  }
53
77
  exports.default = ListCommand;
54
78
  ListCommand.description = "List Actions that Components implement";
55
- ListCommand.flags = { ...core_1.CliUx.ux.table.flags() };
79
+ ListCommand.flags = {
80
+ ...core_1.CliUx.ux.table.flags(),
81
+ public: core_1.Flags.boolean({
82
+ required: false,
83
+ description: "Show actions for the public component with the given key. Use this flag when you have a private component with the same key as a public component.",
84
+ }),
85
+ private: core_1.Flags.boolean({
86
+ required: false,
87
+ description: "Show actions for the private component with the given key. Use this flag when you have a private component with the same key as a public component.",
88
+ }),
89
+ };
90
+ ListCommand.args = [
91
+ {
92
+ name: "Component Key",
93
+ required: true,
94
+ description: "The key of the component to show actions for (e.g. 'salesforce')",
95
+ },
96
+ ];
@@ -15,7 +15,7 @@ const spawnProcess = ([command, ...args], env) => {
15
15
  };
16
16
  class RunCommand extends core_1.Command {
17
17
  async run() {
18
- const { argv, flags: { integrationId, configVarKey }, } = await this.parse(RunCommand);
18
+ const { argv, flags: { integrationId, connectionKey }, } = await this.parse(RunCommand);
19
19
  const result = await (0, graphql_1.gqlRequest)({
20
20
  document: (0, graphql_1.gql) `
21
21
  query integration($id: ID!) {
@@ -42,7 +42,7 @@ class RunCommand extends core_1.Command {
42
42
  },
43
43
  });
44
44
  const nodes = result.integration.testConfigVariables.nodes;
45
- const [connection] = nodes.filter(({ requiredConfigVariable: { key } }) => key === configVarKey);
45
+ const [connection] = nodes.filter(({ requiredConfigVariable: { key } }) => key === connectionKey);
46
46
  if (!connection) {
47
47
  core_1.CliUx.ux.error("Failed to find active connection.", { exit: 1 });
48
48
  }
@@ -56,7 +56,21 @@ class RunCommand extends core_1.Command {
56
56
  }
57
57
  }
58
58
  exports.default = RunCommand;
59
- RunCommand.description = "Run an arbitrary command within a Prismatic context";
59
+ RunCommand.description = `Fetch an integration's active connection and execute a CLI command with that connection's fields as an environment variable.`;
60
+ RunCommand.usage = "prism components:dev:run -i <value> -c <value> -- /command/to/run";
61
+ RunCommand.examples = [
62
+ {
63
+ description: `To simply print an integration's basic auth config variable named "My Credentials" and pipe the resulting JSON to jq, run:`,
64
+ command: `$ prism components:dev:run
65
+ --integrationId SW50ZWdyYXRpb246Y2YyMjJlMWUtMzJiYy00NTNhLWIzOWQtYTliNzUwMDAyNjBk
66
+ --connectionKey "My Credentials" --
67
+ printenv \${PRISMATIC_CONNECTION_VALUE} | jq`,
68
+ },
69
+ {
70
+ description: `If one of your integrations has an authenticated OAuth 2.0 config variable "Slack Connection", you could run your component's unit tests with that environment variable:`,
71
+ command: `$ prism components:dev:run -i SW50ZWexample -c "Slack Connection" -- yarn run test`,
72
+ },
73
+ ];
60
74
  // Allow us to manually capture argv so we can isolate the wrapped command.
61
75
  RunCommand.strict = false;
62
76
  // TODO: Make this derive from the component manifest using the same
@@ -67,9 +81,9 @@ RunCommand.flags = {
67
81
  char: "i",
68
82
  description: "Integration ID",
69
83
  }),
70
- configVarKey: core_1.Flags.string({
84
+ connectionKey: core_1.Flags.string({
71
85
  required: true,
72
86
  char: "c",
73
- description: "Key of the Connection Config Var to fetch meta/state for",
87
+ description: "Key of the connection config variable to fetch meta/state for",
74
88
  }),
75
89
  };
@@ -179,7 +179,7 @@ class TestCommand extends core_1.Command {
179
179
  integrationInfo: {
180
180
  name: (0, definition_1.componentTestIntegrationName)(componentKey, name),
181
181
  },
182
- componentInfo: { key: componentKey, isPublic: isPublic !== null && isPublic !== void 0 ? isPublic : false },
182
+ componentInfo: { key: definition.key, isPublic: isPublic !== null && isPublic !== void 0 ? isPublic : false },
183
183
  actionInfo,
184
184
  connectionInfo,
185
185
  });
@@ -32,31 +32,10 @@ const path = __importStar(require("path"));
32
32
  const wsdl_tsclient_1 = require("wsdl-tsclient");
33
33
  const logger_1 = require("wsdl-tsclient/dist/src/utils/logger");
34
34
  const index_1 = require("../../../generate/index");
35
- const openapi_typescript_codegen_1 = require("openapi-typescript-codegen");
36
35
  const prettier_1 = __importDefault(require("prettier"));
37
36
  const glob_promise_1 = __importDefault(require("glob-promise"));
38
37
  const yeoman_1 = require("../../../yeoman");
39
38
  const componentNameRegex = /^[a-zA-Z0-9][a-zA-Z0-9-_]*[a-zA-Z0-9]$/;
40
- const insertText = /* javascript */ `
41
- interface EnhancedConfig extends Config {
42
- timeout?: number;
43
- CLIENT?: AxiosInstance;
44
- }
45
- export const EnhancedOpenAPI: EnhancedConfig = OpenAPI;`;
46
- const injectAxiosToOpenAPI = async (basePath) => {
47
- const fileLocation = path.resolve(basePath, "core", "request.ts");
48
- const replaceLocation = path.join(__dirname, "..", "..", "..", "..", "templates", "component", "openapi", "request.ts");
49
- await fs_1.promises.copyFile(replaceLocation, fileLocation);
50
- const file = path.resolve(basePath, "core", "OpenAPI.ts");
51
- const contents = await fs_1.promises.readFile(file);
52
- const parts = [contents, insertText].join("\n\n").split("\n");
53
- const additions = [
54
- ...parts.slice(0, 3),
55
- /* javascript */ `import { AxiosInstance } from "axios";`,
56
- ...parts.slice(3),
57
- ].join("\n");
58
- await fs_1.promises.writeFile(file, additions);
59
- };
60
39
  const getFilesToFormat = async (basename) => {
61
40
  return await (0, glob_promise_1.default)("**/*.ts", {
62
41
  ignore: ["**/node_modules/**"],
@@ -73,7 +52,7 @@ const formatSourceFiles = async (basePath, files) => {
73
52
  };
74
53
  class InitializeComponent extends core_1.Command {
75
54
  async run() {
76
- const { args: { name }, flags: { verbose, "wsdl-path": wsdlPath, "open-api-path": openApiPath, "skip-auth": skipAuth, "add-retry": addRetry, }, } = await this.parse(InitializeComponent);
55
+ const { args: { name }, flags: { verbose, "wsdl-path": wsdlPath, "open-api-path": openApiPath }, } = await this.parse(InitializeComponent);
77
56
  if (!componentNameRegex.test(name)) {
78
57
  this.error(`'${name}' contains invalid characters. Please select a component name that starts and ends with alphanumeric characters, and contains only alphanumeric characters, hyphens, and underscores. See https://regex101.com/?regex=${encodeURIComponent(componentNameRegex.source)}`, { exit: 1 });
79
58
  }
@@ -82,81 +61,50 @@ class InitializeComponent extends core_1.Command {
82
61
  exit: 1,
83
62
  });
84
63
  }
85
- if (skipAuth && !openApiPath) {
86
- this.error("Auth templating is only supported when generating components from Open API specifications", {
87
- exit: 1,
88
- });
89
- }
90
- if (addRetry && !openApiPath) {
91
- this.error("HTTP retry logic is only supported when generating components from OpenAPI specifications", {
92
- exit: 1,
93
- });
94
- }
95
64
  this.log(`Creating component directory for "${name}"...`);
96
65
  await fs_1.promises.mkdir(name);
97
66
  const cwd = process.cwd();
98
67
  process.chdir(name);
99
- await (0, yeoman_1.runGenerator)("component", process.env.NODE_ENV === "test"
100
- ? {
68
+ if (openApiPath) {
69
+ await (0, yeoman_1.runGenerator)("formats", {
101
70
  name,
102
- description: "Prism-generated Component",
103
- connectionType: "basic",
104
- skipInstall: true,
105
- }
106
- : { name, skipInstall: Boolean(wsdlPath) || Boolean(openApiPath) });
107
- process.chdir(cwd);
108
- if (wsdlPath) {
109
- if (!verbose) {
110
- // wsdl-tsclient emits pretty noisy logs that aren't particularly useful
111
- logger_1.Logger.disabled();
112
- }
113
- const wsdlName = path.basename(wsdlPath).split(".wsdl")[0];
114
- await (0, wsdl_tsclient_1.parseAndGenerate)(wsdlPath, path.join(name), {
115
- caseInsensitiveNames: true,
116
- });
117
- await (0, index_1.generate)({
118
- projectRoot: name,
119
- projectTemplateName: wsdlName,
120
- projectTemplatePath: wsdlPath,
121
- projectType: "wsdl",
122
- includeClient: false,
123
- addRetry: false,
124
- });
125
- await (0, index_1.updatePackageJson)({
126
- path: path.resolve(name, "package.json"),
127
- dependencies: { soap: "0.40.0" },
71
+ openapi: openApiPath,
128
72
  });
129
73
  }
130
- else if (openApiPath) {
131
- const openApiName = path.basename(openApiPath).split(".")[0];
132
- const basePath = path.resolve(name, openApiName);
133
- await (0, openapi_typescript_codegen_1.generate)({
134
- input: openApiPath,
135
- output: path.join(name, openApiName),
136
- httpClient: openapi_typescript_codegen_1.HttpClient.NODE,
137
- useUnionTypes: true, // handles the case of enums using reserved keywords
138
- });
139
- await (0, index_1.generate)({
140
- projectRoot: name,
141
- projectTemplateName: openApiName,
142
- projectTemplatePath: openApiPath,
143
- projectType: "openApi",
144
- includeClient: !skipAuth,
145
- addRetry,
146
- });
147
- await (0, index_1.updatePackageJson)({
148
- path: path.resolve(name, "package.json"),
149
- dependencies: {
150
- axios: "0.21.4",
151
- "axios-retry": "3.1.9",
152
- "form-data": "4.0.0",
153
- },
154
- });
155
- await injectAxiosToOpenAPI(basePath);
156
- const filesToFormat = await getFilesToFormat(basePath);
157
- await formatSourceFiles(basePath, filesToFormat);
158
- }
159
- this.log(`
74
+ else {
75
+ // Legacy code paths (mostly; keep the component generator call)
76
+ await (0, yeoman_1.runGenerator)("component", process.env.NODE_ENV === "test"
77
+ ? {
78
+ name,
79
+ description: "Prism-generated Component",
80
+ connectionType: "basic",
81
+ skipInstall: true,
82
+ }
83
+ : { name, skipInstall: Boolean(wsdlPath) });
84
+ // Need to pop back as the WSDL generator assumes it's a directory up
85
+ process.chdir(cwd);
86
+ if (wsdlPath) {
87
+ if (!verbose) {
88
+ // wsdl-tsclient emits pretty noisy logs that aren't particularly useful
89
+ logger_1.Logger.disabled();
90
+ }
91
+ const wsdlName = path.basename(wsdlPath).split(".wsdl")[0];
92
+ await (0, wsdl_tsclient_1.parseAndGenerate)(wsdlPath, name, {
93
+ caseInsensitiveNames: true,
94
+ });
95
+ await (0, index_1.generate)({
96
+ projectRoot: name,
97
+ projectTemplateName: wsdlName,
98
+ projectTemplatePath: wsdlPath,
99
+ });
100
+ await (0, index_1.updatePackageJson)({
101
+ path: path.resolve(name, "package.json"),
102
+ dependencies: { soap: "0.40.0" },
103
+ });
104
+ const filesToFormat = await getFilesToFormat(name);
105
+ await formatSourceFiles(name, filesToFormat);
106
+ }
107
+ this.log(`
160
108
  "${name}" is ready for development.
161
109
  To install dependencies, run either "npm install" or "yarn install"
162
110
  To test the component, run "npm run test" or "yarn test"
@@ -165,6 +113,7 @@ To publish the component, run "prism components:publish"
165
113
 
166
114
  For documentation on writing custom components, visit https://prismatic.io/docs/custom-components/writing-custom-components/
167
115
  `);
116
+ }
168
117
  }
169
118
  }
170
119
  exports.default = InitializeComponent;
@@ -178,21 +127,11 @@ InitializeComponent.flags = {
178
127
  required: false,
179
128
  description: "The path to an OpenAPI Specification file (JSON or YAML) used to generate a Component",
180
129
  }),
181
- "skip-auth": core_1.Flags.boolean({
182
- required: false,
183
- default: false,
184
- description: "Skip templating authentication for an OpenAPI-based component. Use this option for OpenAPI specs that do not require authentication.",
185
- }),
186
130
  verbose: core_1.Flags.boolean({
187
131
  required: false,
188
132
  default: false,
189
133
  description: "Output more verbose logging from Component generation",
190
134
  }),
191
- "add-retry": core_1.Flags.boolean({
192
- required: false,
193
- default: false,
194
- description: "Add inputs to each action to utilize Axios retry logic",
195
- }),
196
135
  };
197
136
  InitializeComponent.args = [
198
137
  {
@@ -1,32 +1,50 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const core_1 = require("@oclif/core");
7
+ const dayjs_1 = __importDefault(require("dayjs"));
4
8
  const graphql_1 = require("../../graphql");
5
9
  class ListCommand extends core_1.Command {
6
10
  async run() {
7
11
  const { flags } = await this.parse(ListCommand);
8
12
  const { showAllVersions } = flags;
9
- const result = await (0, graphql_1.gqlRequest)({
10
- document: (0, graphql_1.gql) `
11
- query listComponents($showAllVersions: Boolean) {
12
- components(allVersions: $showAllVersions) {
13
- nodes {
14
- id
15
- key
16
- public
17
- label
18
- description
19
- versionNumber
20
- category
13
+ let components = [];
14
+ let hasNextPage = true;
15
+ let cursor = "";
16
+ while (hasNextPage) {
17
+ const { components: { nodes, pageInfo }, } = await (0, graphql_1.gqlRequest)({
18
+ document: (0, graphql_1.gql) `
19
+ query listComponents($showAllVersions: Boolean, $after: String) {
20
+ components(allVersions: $showAllVersions, after: $after) {
21
+ nodes {
22
+ id
23
+ key
24
+ public
25
+ label
26
+ description
27
+ versionNumber
28
+ category
29
+ versionCreatedAt
30
+ }
31
+ pageInfo {
32
+ hasNextPage
33
+ endCursor
34
+ }
21
35
  }
22
36
  }
37
+ `,
38
+ variables: {
39
+ showAllVersions,
40
+ after: cursor,
41
+ },
42
+ });
43
+ components = [...components, ...nodes];
44
+ cursor = pageInfo.endCursor;
45
+ hasNextPage = pageInfo.hasNextPage;
23
46
  }
24
- `,
25
- variables: {
26
- showAllVersions,
27
- },
28
- });
29
- core_1.CliUx.ux.table(result.components.nodes, {
47
+ core_1.CliUx.ux.table(components, {
30
48
  id: {
31
49
  minWidth: 8,
32
50
  extended: true,
@@ -39,7 +57,12 @@ class ListCommand extends core_1.Command {
39
57
  public: {},
40
58
  description: {},
41
59
  versionNumber: { header: "Version" },
42
- category: {},
60
+ versionCreatedAt: {
61
+ header: "Last Published",
62
+ extended: true,
63
+ get: ({ versionCreatedAt }) => (0, dayjs_1.default)(versionCreatedAt).format(),
64
+ },
65
+ category: { get: ({ category }) => category || "" },
43
66
  }, { ...flags });
44
67
  }
45
68
  }