@eide/uniformgen 0.1.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.
- package/README.md +356 -0
- package/dist/auth/credentials.d.ts +58 -0
- package/dist/auth/credentials.d.ts.map +1 -0
- package/dist/auth/credentials.js +107 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +563 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +113 -0
- package/dist/commands/login.d.ts +9 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +158 -0
- package/dist/commands/logout.d.ts +5 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +13 -0
- package/dist/commands/push.d.ts +13 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +328 -0
- package/dist/commands/scaffold.d.ts +19 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +366 -0
- package/dist/commands/seed.d.ts +20 -0
- package/dist/commands/seed.d.ts.map +1 -0
- package/dist/commands/seed.js +380 -0
- package/dist/commands/select-project.d.ts +10 -0
- package/dist/commands/select-project.d.ts.map +1 -0
- package/dist/commands/select-project.js +277 -0
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +51 -0
- package/dist/commands/sync.d.ts +13 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +318 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +31 -0
- package/dist/config/load-config.d.ts +6 -0
- package/dist/config/load-config.d.ts.map +1 -0
- package/dist/config/load-config.js +103 -0
- package/dist/config/settings.d.ts +20 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +64 -0
- package/dist/config/types.d.ts +152 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +94 -0
- package/dist/fetcher/fetch-schemas.d.ts +140 -0
- package/dist/fetcher/fetch-schemas.d.ts.map +1 -0
- package/dist/fetcher/fetch-schemas.js +223 -0
- package/dist/fetcher/fetch-workflows.d.ts +53 -0
- package/dist/fetcher/fetch-workflows.d.ts.map +1 -0
- package/dist/fetcher/fetch-workflows.js +164 -0
- package/dist/generated/hooks/customer-detail.d.ts +74 -0
- package/dist/generated/hooks/customer-detail.d.ts.map +1 -0
- package/dist/generated/hooks/customer-detail.js +113 -0
- package/dist/generated/hooks/design-system.d.ts +74 -0
- package/dist/generated/hooks/design-system.d.ts.map +1 -0
- package/dist/generated/hooks/design-system.js +109 -0
- package/dist/generated/hooks/index.d.ts +16 -0
- package/dist/generated/hooks/index.d.ts.map +1 -0
- package/dist/generated/hooks/index.js +14 -0
- package/dist/generated/hooks/shopify-collection.d.ts +74 -0
- package/dist/generated/hooks/shopify-collection.d.ts.map +1 -0
- package/dist/generated/hooks/shopify-collection.js +113 -0
- package/dist/generated/hooks/shopify-market.d.ts +74 -0
- package/dist/generated/hooks/shopify-market.d.ts.map +1 -0
- package/dist/generated/hooks/shopify-market.js +109 -0
- package/dist/generated/hooks/shopify-product.d.ts +74 -0
- package/dist/generated/hooks/shopify-product.d.ts.map +1 -0
- package/dist/generated/hooks/shopify-product.js +113 -0
- package/dist/generated/hooks/shopify-variant.d.ts +74 -0
- package/dist/generated/hooks/shopify-variant.d.ts.map +1 -0
- package/dist/generated/hooks/shopify-variant.js +113 -0
- package/dist/generated/hooks/template.d.ts +74 -0
- package/dist/generated/hooks/template.d.ts.map +1 -0
- package/dist/generated/hooks/template.js +107 -0
- package/dist/generated/types/config.d.ts +88 -0
- package/dist/generated/types/config.d.ts.map +1 -0
- package/dist/generated/types/config.js +14 -0
- package/dist/generated/types/data-models/index.d.ts +7 -0
- package/dist/generated/types/data-models/index.d.ts.map +1 -0
- package/dist/generated/types/data-models/index.js +6 -0
- package/dist/generated/types/data-models/test.d.ts +29 -0
- package/dist/generated/types/data-models/test.d.ts.map +1 -0
- package/dist/generated/types/data-models/test.js +1 -0
- package/dist/generated/types/data-models/watch.d.ts +26 -0
- package/dist/generated/types/data-models/watch.d.ts.map +1 -0
- package/dist/generated/types/data-models/watch.js +1 -0
- package/dist/generated/types/field-types.d.ts +255 -0
- package/dist/generated/types/field-types.d.ts.map +1 -0
- package/dist/generated/types/field-types.js +35 -0
- package/dist/generated/types/hooks.d.ts +106 -0
- package/dist/generated/types/hooks.d.ts.map +1 -0
- package/dist/generated/types/hooks.js +9 -0
- package/dist/generated/types/index.d.ts +10 -0
- package/dist/generated/types/index.d.ts.map +1 -0
- package/dist/generated/types/index.js +9 -0
- package/dist/generated/types/models/button-variant.d.ts +16 -0
- package/dist/generated/types/models/button-variant.d.ts.map +1 -0
- package/dist/generated/types/models/button-variant.js +1 -0
- package/dist/generated/types/models/color-palette.d.ts +27 -0
- package/dist/generated/types/models/color-palette.d.ts.map +1 -0
- package/dist/generated/types/models/color-palette.js +1 -0
- package/dist/generated/types/models/color-with-scale.d.ts +15 -0
- package/dist/generated/types/models/color-with-scale.d.ts.map +1 -0
- package/dist/generated/types/models/color-with-scale.js +1 -0
- package/dist/generated/types/models/context.d.ts +53 -0
- package/dist/generated/types/models/context.d.ts.map +1 -0
- package/dist/generated/types/models/context.js +51 -0
- package/dist/generated/types/models/customer-detail.d.ts +32 -0
- package/dist/generated/types/models/customer-detail.d.ts.map +1 -0
- package/dist/generated/types/models/customer-detail.js +26 -0
- package/dist/generated/types/models/design-system-badges.d.ts +16 -0
- package/dist/generated/types/models/design-system-badges.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-badges.js +1 -0
- package/dist/generated/types/models/design-system-buttons.d.ts +17 -0
- package/dist/generated/types/models/design-system-buttons.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-buttons.js +1 -0
- package/dist/generated/types/models/design-system-cards.d.ts +16 -0
- package/dist/generated/types/models/design-system-cards.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-cards.js +1 -0
- package/dist/generated/types/models/design-system-colors.d.ts +12 -0
- package/dist/generated/types/models/design-system-colors.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-colors.js +1 -0
- package/dist/generated/types/models/design-system-dark-mode.d.ts +13 -0
- package/dist/generated/types/models/design-system-dark-mode.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-dark-mode.js +1 -0
- package/dist/generated/types/models/design-system-focus-ring.d.ts +14 -0
- package/dist/generated/types/models/design-system-focus-ring.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-focus-ring.js +1 -0
- package/dist/generated/types/models/design-system-grid.d.ts +18 -0
- package/dist/generated/types/models/design-system-grid.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-grid.js +1 -0
- package/dist/generated/types/models/design-system-inputs.d.ts +26 -0
- package/dist/generated/types/models/design-system-inputs.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-inputs.js +1 -0
- package/dist/generated/types/models/design-system-links.d.ts +16 -0
- package/dist/generated/types/models/design-system-links.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-links.js +1 -0
- package/dist/generated/types/models/design-system-shadows.d.ts +20 -0
- package/dist/generated/types/models/design-system-shadows.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-shadows.js +1 -0
- package/dist/generated/types/models/design-system-spacing.d.ts +13 -0
- package/dist/generated/types/models/design-system-spacing.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-spacing.js +1 -0
- package/dist/generated/types/models/design-system-transitions.d.ts +15 -0
- package/dist/generated/types/models/design-system-transitions.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-transitions.js +1 -0
- package/dist/generated/types/models/design-system-typography.d.ts +21 -0
- package/dist/generated/types/models/design-system-typography.d.ts.map +1 -0
- package/dist/generated/types/models/design-system-typography.js +1 -0
- package/dist/generated/types/models/design-system.d.ts +138 -0
- package/dist/generated/types/models/design-system.d.ts.map +1 -0
- package/dist/generated/types/models/design-system.js +64 -0
- package/dist/generated/types/models/experiment.d.ts +57 -0
- package/dist/generated/types/models/experiment.d.ts.map +1 -0
- package/dist/generated/types/models/experiment.js +55 -0
- package/dist/generated/types/models/font-file.d.ts +16 -0
- package/dist/generated/types/models/font-file.d.ts.map +1 -0
- package/dist/generated/types/models/font-file.js +1 -0
- package/dist/generated/types/models/index.d.ts +38 -0
- package/dist/generated/types/models/index.d.ts.map +1 -0
- package/dist/generated/types/models/index.js +12 -0
- package/dist/generated/types/models/integration.d.ts +32 -0
- package/dist/generated/types/models/integration.d.ts.map +1 -0
- package/dist/generated/types/models/integration.js +26 -0
- package/dist/generated/types/models/route-tree.d.ts +43 -0
- package/dist/generated/types/models/route-tree.d.ts.map +1 -0
- package/dist/generated/types/models/route-tree.js +39 -0
- package/dist/generated/types/models/segment.d.ts +57 -0
- package/dist/generated/types/models/segment.d.ts.map +1 -0
- package/dist/generated/types/models/segment.js +55 -0
- package/dist/generated/types/models/shopify-collection.d.ts +32 -0
- package/dist/generated/types/models/shopify-collection.d.ts.map +1 -0
- package/dist/generated/types/models/shopify-collection.js +26 -0
- package/dist/generated/types/models/shopify-market.d.ts +32 -0
- package/dist/generated/types/models/shopify-market.d.ts.map +1 -0
- package/dist/generated/types/models/shopify-market.js +26 -0
- package/dist/generated/types/models/shopify-product.d.ts +32 -0
- package/dist/generated/types/models/shopify-product.d.ts.map +1 -0
- package/dist/generated/types/models/shopify-product.js +26 -0
- package/dist/generated/types/models/shopify-variant.d.ts +32 -0
- package/dist/generated/types/models/shopify-variant.d.ts.map +1 -0
- package/dist/generated/types/models/shopify-variant.js +26 -0
- package/dist/generated/types/models/template.d.ts +53 -0
- package/dist/generated/types/models/template.d.ts.map +1 -0
- package/dist/generated/types/models/template.js +40 -0
- package/dist/generated/types/models/typography-variant.d.ts +15 -0
- package/dist/generated/types/models/typography-variant.d.ts.map +1 -0
- package/dist/generated/types/models/typography-variant.js +1 -0
- package/dist/generated/types/scalars.d.ts +56 -0
- package/dist/generated/types/scalars.d.ts.map +1 -0
- package/dist/generated/types/scalars.js +6 -0
- package/dist/generators/admin/index.d.ts +32 -0
- package/dist/generators/admin/index.d.ts.map +1 -0
- package/dist/generators/admin/index.js +219 -0
- package/dist/generators/admin/mutations.d.ts +23 -0
- package/dist/generators/admin/mutations.d.ts.map +1 -0
- package/dist/generators/admin/mutations.js +424 -0
- package/dist/generators/admin/queries.d.ts +20 -0
- package/dist/generators/admin/queries.d.ts.map +1 -0
- package/dist/generators/admin/queries.js +476 -0
- package/dist/generators/admin/types.d.ts +28 -0
- package/dist/generators/admin/types.d.ts.map +1 -0
- package/dist/generators/admin/types.js +254 -0
- package/dist/generators/cms/index.d.ts +29 -0
- package/dist/generators/cms/index.d.ts.map +1 -0
- package/dist/generators/cms/index.js +126 -0
- package/dist/generators/cms/route.d.ts +27 -0
- package/dist/generators/cms/route.d.ts.map +1 -0
- package/dist/generators/cms/route.js +409 -0
- package/dist/generators/cms/types.d.ts +15 -0
- package/dist/generators/cms/types.d.ts.map +1 -0
- package/dist/generators/cms/types.js +137 -0
- package/dist/generators/contexts/index.d.ts +25 -0
- package/dist/generators/contexts/index.d.ts.map +1 -0
- package/dist/generators/contexts/index.js +591 -0
- package/dist/generators/documents/data-models.d.ts +6 -0
- package/dist/generators/documents/data-models.d.ts.map +1 -0
- package/dist/generators/documents/data-models.js +61 -0
- package/dist/generators/documents/entity-models.d.ts +7 -0
- package/dist/generators/documents/entity-models.d.ts.map +1 -0
- package/dist/generators/documents/entity-models.js +87 -0
- package/dist/generators/documents/workflows.d.ts +11 -0
- package/dist/generators/documents/workflows.d.ts.map +1 -0
- package/dist/generators/documents/workflows.js +101 -0
- package/dist/generators/filters/index.d.ts +16 -0
- package/dist/generators/filters/index.d.ts.map +1 -0
- package/dist/generators/filters/index.js +384 -0
- package/dist/generators/hooks/agnostic.d.ts +16 -0
- package/dist/generators/hooks/agnostic.d.ts.map +1 -0
- package/dist/generators/hooks/agnostic.js +248 -0
- package/dist/generators/hooks/index.d.ts +9 -0
- package/dist/generators/hooks/index.d.ts.map +1 -0
- package/dist/generators/hooks/index.js +8 -0
- package/dist/generators/hooks/react.d.ts +16 -0
- package/dist/generators/hooks/react.d.ts.map +1 -0
- package/dist/generators/hooks/react.js +394 -0
- package/dist/generators/hooks/remix.d.ts +16 -0
- package/dist/generators/hooks/remix.d.ts.map +1 -0
- package/dist/generators/hooks/remix.js +349 -0
- package/dist/generators/hooks/workflows.d.ts +23 -0
- package/dist/generators/hooks/workflows.d.ts.map +1 -0
- package/dist/generators/hooks/workflows.js +312 -0
- package/dist/generators/resolve/index.d.ts +13 -0
- package/dist/generators/resolve/index.d.ts.map +1 -0
- package/dist/generators/resolve/index.js +13 -0
- package/dist/generators/resolve/platform.d.ts +29 -0
- package/dist/generators/resolve/platform.d.ts.map +1 -0
- package/dist/generators/resolve/platform.js +479 -0
- package/dist/generators/types/config.d.ts +7 -0
- package/dist/generators/types/config.d.ts.map +1 -0
- package/dist/generators/types/config.js +113 -0
- package/dist/generators/types/data-models.d.ts +10 -0
- package/dist/generators/types/data-models.d.ts.map +1 -0
- package/dist/generators/types/data-models.js +100 -0
- package/dist/generators/types/entity-models.d.ts +13 -0
- package/dist/generators/types/entity-models.d.ts.map +1 -0
- package/dist/generators/types/entity-models.js +241 -0
- package/dist/generators/types/field-types.d.ts +9 -0
- package/dist/generators/types/field-types.d.ts.map +1 -0
- package/dist/generators/types/field-types.js +651 -0
- package/dist/generators/types/hooks.d.ts +7 -0
- package/dist/generators/types/hooks.d.ts.map +1 -0
- package/dist/generators/types/hooks.js +132 -0
- package/dist/generators/types/scalars.d.ts +6 -0
- package/dist/generators/types/scalars.d.ts.map +1 -0
- package/dist/generators/types/scalars.js +68 -0
- package/dist/generators/types/user-details.d.ts +6 -0
- package/dist/generators/types/user-details.d.ts.map +1 -0
- package/dist/generators/types/user-details.js +60 -0
- package/dist/generators/types/workflows.d.ts +15 -0
- package/dist/generators/types/workflows.d.ts.map +1 -0
- package/dist/generators/types/workflows.js +163 -0
- package/dist/graphql/generated/gql.d.ts +47 -0
- package/dist/graphql/generated/gql.d.ts.map +1 -0
- package/dist/graphql/generated/gql.js +10 -0
- package/dist/graphql/generated/graphql.d.ts +8455 -0
- package/dist/graphql/generated/graphql.d.ts.map +1 -0
- package/dist/graphql/generated/graphql.js +573 -0
- package/dist/graphql/generated/index.d.ts +2 -0
- package/dist/graphql/generated/index.d.ts.map +1 -0
- package/dist/graphql/generated/index.js +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/schema/define-entity-model.d.ts +122 -0
- package/dist/schema/define-entity-model.d.ts.map +1 -0
- package/dist/schema/define-entity-model.js +59 -0
- package/dist/schema/schema-loader.d.ts +10 -0
- package/dist/schema/schema-loader.d.ts.map +1 -0
- package/dist/schema/schema-loader.js +91 -0
- package/dist/utils/field-mapping.d.ts +83 -0
- package/dist/utils/field-mapping.d.ts.map +1 -0
- package/dist/utils/field-mapping.js +334 -0
- package/dist/writer/write-files.d.ts +12 -0
- package/dist/writer/write-files.d.ts.map +1 -0
- package/dist/writer/write-files.js +35 -0
- package/package.json +70 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Command
|
|
3
|
+
*
|
|
4
|
+
* Seeds content data to a Foir project from local JSON/YAML files.
|
|
5
|
+
* Useful for bootstrapping starter content in templates.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
10
|
+
import { join, extname, basename } from 'path';
|
|
11
|
+
import { loadConfig } from '../config/load-config.js';
|
|
12
|
+
/**
|
|
13
|
+
* Get the GraphQL endpoint URL
|
|
14
|
+
*/
|
|
15
|
+
function getGraphQLEndpoint(apiUrl) {
|
|
16
|
+
const base = apiUrl.replace(/\/$/, '').replace(/\/graphql$/, '');
|
|
17
|
+
return `${base}/graphql`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a simple GraphQL client
|
|
21
|
+
*/
|
|
22
|
+
function createClient(apiUrl, accessToken, tenantId, projectId) {
|
|
23
|
+
const endpoint = getGraphQLEndpoint(apiUrl);
|
|
24
|
+
return {
|
|
25
|
+
async request(query, variables) {
|
|
26
|
+
const headers = {
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
};
|
|
29
|
+
if (accessToken) {
|
|
30
|
+
headers['Authorization'] = `Bearer ${accessToken}`;
|
|
31
|
+
}
|
|
32
|
+
// Add tenant/project context headers for internal API
|
|
33
|
+
if (tenantId) {
|
|
34
|
+
headers['x-tenant-id'] = tenantId;
|
|
35
|
+
}
|
|
36
|
+
if (projectId) {
|
|
37
|
+
headers['x-project-id'] = projectId;
|
|
38
|
+
}
|
|
39
|
+
const response = await fetch(endpoint, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers,
|
|
42
|
+
body: JSON.stringify({ query, variables }),
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
46
|
+
}
|
|
47
|
+
const result = await response.json();
|
|
48
|
+
if (result.errors && result.errors.length > 0) {
|
|
49
|
+
throw new Error(result.errors.map((e) => e.message).join(', '));
|
|
50
|
+
}
|
|
51
|
+
return result.data;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// GraphQL mutations (Internal API)
|
|
56
|
+
const CREATE_ENTITY_RECORD_MUTATION = `
|
|
57
|
+
mutation CreateEntityRecord($input: CreateEntityRecordInput!) {
|
|
58
|
+
createEntityRecord(input: $input) {
|
|
59
|
+
id
|
|
60
|
+
modelKey
|
|
61
|
+
naturalKey
|
|
62
|
+
currentVersionId
|
|
63
|
+
variants(limit: 1) {
|
|
64
|
+
items {
|
|
65
|
+
id
|
|
66
|
+
currentVersionId
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
const PUBLISH_VERSION_MUTATION = `
|
|
73
|
+
mutation PublishEntityRecordVersion($versionId: ID!) {
|
|
74
|
+
publishEntityRecordVersion(versionId: $versionId) {
|
|
75
|
+
id
|
|
76
|
+
publishedVersionId
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
const CREATE_ROUTE_MUTATION = `
|
|
81
|
+
mutation CreateRoute($input: CreateRouteInput!) {
|
|
82
|
+
createRoute(input: $input) {
|
|
83
|
+
id
|
|
84
|
+
path
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
// Query to find existing record by natural key
|
|
89
|
+
const GET_ENTITY_RECORD_QUERY = `
|
|
90
|
+
query GetEntityRecord($modelKey: String!, $naturalKey: String!) {
|
|
91
|
+
entityRecord(modelKey: $modelKey, naturalKey: $naturalKey) {
|
|
92
|
+
id
|
|
93
|
+
naturalKey
|
|
94
|
+
currentVersionId
|
|
95
|
+
variants(limit: 1) {
|
|
96
|
+
items {
|
|
97
|
+
id
|
|
98
|
+
currentVersionId
|
|
99
|
+
isDefault
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
// Mutation to create a new version (for upsert)
|
|
106
|
+
const CREATE_VERSION_MUTATION = `
|
|
107
|
+
mutation CreateEntityRecordVersion($input: CreateEntityRecordVersionInput!) {
|
|
108
|
+
createEntityRecordVersion(input: $input) {
|
|
109
|
+
id
|
|
110
|
+
versionNumber
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
// Mutation to update record metadata
|
|
115
|
+
const UPDATE_ENTITY_RECORD_MUTATION = `
|
|
116
|
+
mutation UpdateEntityRecord($id: ID!, $input: UpdateEntityRecordInput!) {
|
|
117
|
+
updateEntityRecord(id: $id, input: $input) {
|
|
118
|
+
id
|
|
119
|
+
naturalKey
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
/**
|
|
124
|
+
* Parse a seed file (JSON or YAML)
|
|
125
|
+
*/
|
|
126
|
+
async function parseSeedFile(filePath) {
|
|
127
|
+
const ext = extname(filePath).toLowerCase();
|
|
128
|
+
const content = await readFile(filePath, 'utf-8');
|
|
129
|
+
if (ext === '.json') {
|
|
130
|
+
return JSON.parse(content);
|
|
131
|
+
}
|
|
132
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
133
|
+
// Simple YAML parsing for common cases
|
|
134
|
+
// For full YAML support, users can install js-yaml
|
|
135
|
+
try {
|
|
136
|
+
// Dynamic import for optional js-yaml dependency
|
|
137
|
+
// @ts-expect-error - js-yaml is an optional peer dependency
|
|
138
|
+
const jsYaml = await import('js-yaml');
|
|
139
|
+
const loadFn = jsYaml.load || jsYaml.default?.load;
|
|
140
|
+
if (!loadFn)
|
|
141
|
+
throw new Error('js-yaml load function not found');
|
|
142
|
+
return loadFn(content);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
console.warn(chalk.yellow(` Warning: js-yaml not installed, skipping ${filePath}`));
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get all seed files from a directory
|
|
153
|
+
*/
|
|
154
|
+
async function getSeedFiles(seedDir, pattern) {
|
|
155
|
+
const files = [];
|
|
156
|
+
async function scanDir(dir) {
|
|
157
|
+
const entries = await readdir(dir);
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
const fullPath = join(dir, entry);
|
|
160
|
+
const stats = await stat(fullPath);
|
|
161
|
+
if (stats.isDirectory()) {
|
|
162
|
+
await scanDir(fullPath);
|
|
163
|
+
}
|
|
164
|
+
else if (stats.isFile()) {
|
|
165
|
+
const ext = extname(entry).toLowerCase();
|
|
166
|
+
if (['.json', '.yaml', '.yml'].includes(ext)) {
|
|
167
|
+
// Apply pattern filter if provided
|
|
168
|
+
if (!pattern || entry.includes(pattern) || basename(entry, ext).includes(pattern)) {
|
|
169
|
+
files.push(fullPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
await scanDir(seedDir);
|
|
176
|
+
return files.sort();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Main seed function
|
|
180
|
+
*/
|
|
181
|
+
export async function seed(options) {
|
|
182
|
+
const spinner = ora();
|
|
183
|
+
try {
|
|
184
|
+
// Load configuration
|
|
185
|
+
spinner.start('Loading configuration...');
|
|
186
|
+
const config = await loadConfig(options.config, process.cwd());
|
|
187
|
+
// Override config with CLI options
|
|
188
|
+
if (options.apiUrl)
|
|
189
|
+
config.apiUrl = options.apiUrl;
|
|
190
|
+
if (!config.apiUrl) {
|
|
191
|
+
throw new Error('API URL not configured. Run `uniformgen setup` or provide --api-url');
|
|
192
|
+
}
|
|
193
|
+
spinner.succeed('Configuration loaded');
|
|
194
|
+
// Create GraphQL client with tenant/project context
|
|
195
|
+
const client = createClient(config.apiUrl, config.accessToken, config.tenantId, config.projectId);
|
|
196
|
+
// Find seed files
|
|
197
|
+
spinner.start(`Scanning ${options.seedDir} for seed files...`);
|
|
198
|
+
const seedFiles = await getSeedFiles(options.seedDir, options.pattern);
|
|
199
|
+
spinner.succeed(`Found ${seedFiles.length} seed files`);
|
|
200
|
+
if (seedFiles.length === 0) {
|
|
201
|
+
console.log(chalk.yellow('\nNo seed files found.'));
|
|
202
|
+
console.log(chalk.gray('Create .json or .yaml files in the seed directory.'));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Process each seed file
|
|
206
|
+
let totalCreated = 0;
|
|
207
|
+
let totalUpdated = 0;
|
|
208
|
+
let totalPublished = 0;
|
|
209
|
+
let totalRoutes = 0;
|
|
210
|
+
let totalSkipped = 0;
|
|
211
|
+
let totalErrors = 0;
|
|
212
|
+
for (const filePath of seedFiles) {
|
|
213
|
+
const relativePath = filePath.replace(options.seedDir, '').replace(/^\//, '');
|
|
214
|
+
spinner.start(`Processing ${relativePath}...`);
|
|
215
|
+
const seedData = await parseSeedFile(filePath);
|
|
216
|
+
if (!seedData) {
|
|
217
|
+
spinner.warn(`Skipped ${relativePath} (unsupported format)`);
|
|
218
|
+
totalSkipped++;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const { modelKey, records } = seedData;
|
|
222
|
+
if (!modelKey || !records || !Array.isArray(records)) {
|
|
223
|
+
spinner.warn(`Skipped ${relativePath} (invalid format - needs modelKey and records)`);
|
|
224
|
+
totalSkipped++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
spinner.text = `Processing ${relativePath} (${records.length} records)...`;
|
|
228
|
+
for (const record of records) {
|
|
229
|
+
if (!record.naturalKey || !record.data) {
|
|
230
|
+
console.log(chalk.yellow(` Skipping record without naturalKey or data`));
|
|
231
|
+
totalSkipped++;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
if (options.dryRun) {
|
|
236
|
+
console.log(chalk.gray(` [dry-run] Would ${options.upsert ? 'upsert' : 'create'} ${modelKey}/${record.naturalKey}`));
|
|
237
|
+
totalCreated++;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
let existingRecord = null;
|
|
241
|
+
if (options.upsert) {
|
|
242
|
+
try {
|
|
243
|
+
const result = await client.request(GET_ENTITY_RECORD_QUERY, {
|
|
244
|
+
modelKey,
|
|
245
|
+
naturalKey: record.naturalKey,
|
|
246
|
+
});
|
|
247
|
+
existingRecord = result.entityRecord;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// Record doesn't exist, will create new
|
|
251
|
+
existingRecord = null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let entityId;
|
|
255
|
+
let versionId = null;
|
|
256
|
+
if (existingRecord !== null && options.upsert) {
|
|
257
|
+
// UPDATE existing record
|
|
258
|
+
const existing = existingRecord;
|
|
259
|
+
entityId = existing.id;
|
|
260
|
+
// Update metadata if provided
|
|
261
|
+
if (record.metadata) {
|
|
262
|
+
await client.request(UPDATE_ENTITY_RECORD_MUTATION, {
|
|
263
|
+
id: entityId,
|
|
264
|
+
input: { metadata: record.metadata },
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// Get the default variant ID
|
|
268
|
+
const defaultVariant = existing.variants.items.find((v) => v.isDefault) || existing.variants.items[0];
|
|
269
|
+
if (defaultVariant) {
|
|
270
|
+
// Create a new version with the seed content
|
|
271
|
+
const versionResult = await client.request(CREATE_VERSION_MUTATION, {
|
|
272
|
+
input: {
|
|
273
|
+
variantId: defaultVariant.id,
|
|
274
|
+
content: record.data,
|
|
275
|
+
changeDescription: 'Updated via seed --upsert',
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
versionId = versionResult.createEntityRecordVersion.id;
|
|
279
|
+
}
|
|
280
|
+
totalUpdated++;
|
|
281
|
+
console.log(chalk.blue(` ↻ ${modelKey}/${record.naturalKey} (updated)`));
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// CREATE new record
|
|
285
|
+
const createResult = await client.request(CREATE_ENTITY_RECORD_MUTATION, {
|
|
286
|
+
input: {
|
|
287
|
+
modelKey,
|
|
288
|
+
naturalKey: record.naturalKey,
|
|
289
|
+
metadata: record.metadata,
|
|
290
|
+
content: record.data,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
entityId = createResult.createEntityRecord.id;
|
|
294
|
+
versionId =
|
|
295
|
+
createResult.createEntityRecord.currentVersionId ??
|
|
296
|
+
createResult.createEntityRecord.variants.items[0]?.currentVersionId ?? null;
|
|
297
|
+
totalCreated++;
|
|
298
|
+
console.log(chalk.green(` ✓ ${modelKey}/${record.naturalKey}`));
|
|
299
|
+
}
|
|
300
|
+
// Create route if routing config provided
|
|
301
|
+
if (record.routing?.path) {
|
|
302
|
+
try {
|
|
303
|
+
await client.request(CREATE_ROUTE_MUTATION, {
|
|
304
|
+
input: {
|
|
305
|
+
path: record.routing.path,
|
|
306
|
+
entityRecordId: entityId,
|
|
307
|
+
priority: record.routing.priority ?? 0,
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
totalRoutes++;
|
|
311
|
+
}
|
|
312
|
+
catch (_routeError) {
|
|
313
|
+
// Route creation is optional, log but don't fail
|
|
314
|
+
console.log(chalk.yellow(` Warning: Could not create route for ${record.naturalKey}`));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Publish if requested
|
|
318
|
+
if (options.publish && versionId) {
|
|
319
|
+
try {
|
|
320
|
+
await client.request(PUBLISH_VERSION_MUTATION, {
|
|
321
|
+
versionId,
|
|
322
|
+
});
|
|
323
|
+
totalPublished++;
|
|
324
|
+
}
|
|
325
|
+
catch (_publishError) {
|
|
326
|
+
console.log(chalk.yellow(` Warning: Could not publish ${record.naturalKey}`));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
332
|
+
if (message.includes('already exists') || message.includes('duplicate')) {
|
|
333
|
+
if (options.upsert) {
|
|
334
|
+
console.log(chalk.red(` ✗ ${modelKey}/${record.naturalKey}: Failed to upsert - ${message}`));
|
|
335
|
+
totalErrors++;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
console.log(chalk.gray(` ○ ${modelKey}/${record.naturalKey} (already exists, use --upsert to update)`));
|
|
339
|
+
totalSkipped++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
console.log(chalk.red(` ✗ ${modelKey}/${record.naturalKey}: ${message}`));
|
|
344
|
+
totalErrors++;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
spinner.succeed(`Processed ${relativePath}`);
|
|
349
|
+
}
|
|
350
|
+
// Summary
|
|
351
|
+
console.log('');
|
|
352
|
+
console.log(chalk.bold('Seed Summary'));
|
|
353
|
+
console.log('─'.repeat(40));
|
|
354
|
+
console.log(chalk.green(` Created: ${totalCreated} records`));
|
|
355
|
+
if (totalUpdated > 0) {
|
|
356
|
+
console.log(chalk.blue(` Updated: ${totalUpdated} records`));
|
|
357
|
+
}
|
|
358
|
+
if (options.publish) {
|
|
359
|
+
console.log(chalk.green(` Published: ${totalPublished} records`));
|
|
360
|
+
}
|
|
361
|
+
if (totalRoutes > 0) {
|
|
362
|
+
console.log(chalk.green(` Routes: ${totalRoutes} created`));
|
|
363
|
+
}
|
|
364
|
+
if (totalSkipped > 0) {
|
|
365
|
+
console.log(chalk.yellow(` Skipped: ${totalSkipped} records`));
|
|
366
|
+
}
|
|
367
|
+
if (totalErrors > 0) {
|
|
368
|
+
console.log(chalk.red(` Errors: ${totalErrors} records`));
|
|
369
|
+
}
|
|
370
|
+
if (options.dryRun) {
|
|
371
|
+
console.log('');
|
|
372
|
+
console.log(chalk.cyan('This was a dry run. No changes were made.'));
|
|
373
|
+
console.log(chalk.gray('Remove --dry-run to actually seed data.'));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
spinner.fail('Seeding failed');
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface SelectProjectOptions {
|
|
2
|
+
apiUrl: string;
|
|
3
|
+
projectId?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Select project command - choose which project to work with
|
|
7
|
+
*/
|
|
8
|
+
export declare function selectProject(options: SelectProjectOptions): Promise<void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=select-project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-project.d.ts","sourceRoot":"","sources":["../../src/commands/select-project.ts"],"names":[],"mappings":"AA2BA,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+QD;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Ff"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import { getCredentials, updateCredentials } from '../auth/credentials.js';
|
|
3
|
+
const CLI_API_KEY_NAME = 'UniformGen CLI';
|
|
4
|
+
/**
|
|
5
|
+
* Get the internal GraphQL endpoint URL from the base API URL
|
|
6
|
+
*/
|
|
7
|
+
function getInternalGraphQLEndpoint(apiUrl) {
|
|
8
|
+
const base = apiUrl.replace(/\/$/, '').replace(/\/graphql$/, '');
|
|
9
|
+
return `${base}/graphql`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fetch session context with available tenants and projects
|
|
13
|
+
* Uses the same pattern as the admin app - no tenant context required
|
|
14
|
+
*/
|
|
15
|
+
async function fetchSessionContext(apiUrl, accessToken) {
|
|
16
|
+
const response = await fetch(getInternalGraphQLEndpoint(apiUrl), {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
Authorization: `Bearer ${accessToken}`,
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
query: `
|
|
24
|
+
query GetSessionContext {
|
|
25
|
+
sessionContext {
|
|
26
|
+
tenantId
|
|
27
|
+
projectId
|
|
28
|
+
availableTenants {
|
|
29
|
+
id
|
|
30
|
+
name
|
|
31
|
+
}
|
|
32
|
+
availableProjects {
|
|
33
|
+
id
|
|
34
|
+
name
|
|
35
|
+
tenantId
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error(`Failed to fetch session context: ${response.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
const result = (await response.json());
|
|
46
|
+
if (result.errors && result.errors.length > 0) {
|
|
47
|
+
throw new Error(`GraphQL error: ${result.errors[0]?.message ?? 'Unknown error'}`);
|
|
48
|
+
}
|
|
49
|
+
if (!result.data?.sessionContext) {
|
|
50
|
+
throw new Error('No session context returned');
|
|
51
|
+
}
|
|
52
|
+
return result.data.sessionContext;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fetch API keys for a project
|
|
56
|
+
*/
|
|
57
|
+
async function fetchApiKeys(apiUrl, accessToken, projectId, tenantId) {
|
|
58
|
+
const response = await fetch(getInternalGraphQLEndpoint(apiUrl), {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
Authorization: `Bearer ${accessToken}`,
|
|
63
|
+
'x-tenant-id': tenantId,
|
|
64
|
+
'x-project-id': projectId,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
query: `
|
|
68
|
+
query ListApiKeys {
|
|
69
|
+
listApiKeys(includeInactive: false, limit: 100) {
|
|
70
|
+
apiKeys {
|
|
71
|
+
id
|
|
72
|
+
name
|
|
73
|
+
isActive
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`Failed to fetch API keys: ${response.statusText}`);
|
|
82
|
+
}
|
|
83
|
+
const result = (await response.json());
|
|
84
|
+
if (result.errors && result.errors.length > 0) {
|
|
85
|
+
throw new Error(`GraphQL error: ${result.errors[0]?.message ?? 'Unknown error'}`);
|
|
86
|
+
}
|
|
87
|
+
return result.data?.listApiKeys?.apiKeys ?? [];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a new API key
|
|
91
|
+
*/
|
|
92
|
+
async function createApiKey(apiUrl, accessToken, projectId, tenantId, name) {
|
|
93
|
+
const response = await fetch(getInternalGraphQLEndpoint(apiUrl), {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
headers: {
|
|
96
|
+
'Content-Type': 'application/json',
|
|
97
|
+
Authorization: `Bearer ${accessToken}`,
|
|
98
|
+
'x-tenant-id': tenantId,
|
|
99
|
+
'x-project-id': projectId,
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
query: `
|
|
103
|
+
mutation CreateApiKey($input: CreateApiKeyInput!) {
|
|
104
|
+
createApiKey(input: $input) {
|
|
105
|
+
apiKey {
|
|
106
|
+
id
|
|
107
|
+
name
|
|
108
|
+
isActive
|
|
109
|
+
}
|
|
110
|
+
plainKey
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
`,
|
|
114
|
+
variables: {
|
|
115
|
+
input: {
|
|
116
|
+
name,
|
|
117
|
+
projectId,
|
|
118
|
+
scopes: ['entities:read', 'files:read'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
throw new Error(`Failed to create API key: ${response.statusText}`);
|
|
125
|
+
}
|
|
126
|
+
const result = (await response.json());
|
|
127
|
+
if (result.errors && result.errors.length > 0) {
|
|
128
|
+
throw new Error(`GraphQL error: ${result.errors[0]?.message ?? 'Unknown error'}`);
|
|
129
|
+
}
|
|
130
|
+
if (!result.data?.createApiKey) {
|
|
131
|
+
throw new Error('Failed to create API key: No data returned');
|
|
132
|
+
}
|
|
133
|
+
return result.data.createApiKey;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Rotate an existing API key to get a new plain key
|
|
137
|
+
*/
|
|
138
|
+
async function rotateApiKey(apiUrl, accessToken, projectId, tenantId, keyId) {
|
|
139
|
+
const response = await fetch(getInternalGraphQLEndpoint(apiUrl), {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'application/json',
|
|
143
|
+
Authorization: `Bearer ${accessToken}`,
|
|
144
|
+
'x-tenant-id': tenantId,
|
|
145
|
+
'x-project-id': projectId,
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
query: `
|
|
149
|
+
mutation RotateApiKey($id: ID!) {
|
|
150
|
+
rotateApiKey(id: $id) {
|
|
151
|
+
apiKey {
|
|
152
|
+
id
|
|
153
|
+
name
|
|
154
|
+
isActive
|
|
155
|
+
}
|
|
156
|
+
plainKey
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`,
|
|
160
|
+
variables: { id: keyId },
|
|
161
|
+
}),
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Failed to rotate API key: ${response.statusText}`);
|
|
165
|
+
}
|
|
166
|
+
const result = (await response.json());
|
|
167
|
+
if (result.errors && result.errors.length > 0) {
|
|
168
|
+
throw new Error(`GraphQL error: ${result.errors[0]?.message ?? 'Unknown error'}`);
|
|
169
|
+
}
|
|
170
|
+
if (!result.data?.rotateApiKey) {
|
|
171
|
+
throw new Error('Failed to rotate API key: No data returned');
|
|
172
|
+
}
|
|
173
|
+
return result.data.rotateApiKey;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Provision an API key for CLI usage
|
|
177
|
+
* - If CLI key exists and we have it stored, verify it's still active
|
|
178
|
+
* - If CLI key exists but we don't have it, rotate to get new plain key
|
|
179
|
+
* - If CLI key doesn't exist, create one
|
|
180
|
+
*/
|
|
181
|
+
async function provisionApiKey(apiUrl, accessToken, projectId, tenantId) {
|
|
182
|
+
const apiKeys = await fetchApiKeys(apiUrl, accessToken, projectId, tenantId);
|
|
183
|
+
const existingCliKey = apiKeys.find((k) => k.name === CLI_API_KEY_NAME && k.isActive);
|
|
184
|
+
if (existingCliKey) {
|
|
185
|
+
// Key exists - rotate it to get a new plain key
|
|
186
|
+
// (We can't retrieve the plain key after creation, only on create/rotate)
|
|
187
|
+
console.log(' Rotating existing CLI API key...');
|
|
188
|
+
const rotated = await rotateApiKey(apiUrl, accessToken, projectId, tenantId, existingCliKey.id);
|
|
189
|
+
return {
|
|
190
|
+
apiKey: rotated.plainKey,
|
|
191
|
+
apiKeyId: rotated.apiKey.id,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// No CLI key exists - create one
|
|
196
|
+
console.log(' Creating CLI API key...');
|
|
197
|
+
const created = await createApiKey(apiUrl, accessToken, projectId, tenantId, CLI_API_KEY_NAME);
|
|
198
|
+
return {
|
|
199
|
+
apiKey: created.plainKey,
|
|
200
|
+
apiKeyId: created.apiKey.id,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Select project command - choose which project to work with
|
|
206
|
+
*/
|
|
207
|
+
export async function selectProject(options) {
|
|
208
|
+
const credentials = await getCredentials();
|
|
209
|
+
if (!credentials) {
|
|
210
|
+
console.log('Not logged in. Run `uniformgen login` first.');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
console.log('Fetching your projects...\n');
|
|
214
|
+
// Fetch session context - same pattern as admin app
|
|
215
|
+
const sessionContext = await fetchSessionContext(options.apiUrl, credentials.accessToken);
|
|
216
|
+
const { availableTenants: tenants, availableProjects: projects } = sessionContext;
|
|
217
|
+
if (projects.length === 0) {
|
|
218
|
+
console.log('No projects found. Create one in the platform first.');
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
// Create tenant name lookup map
|
|
222
|
+
const tenantNameMap = new Map(tenants.map((t) => [t.id, t.name]));
|
|
223
|
+
let selectedProject;
|
|
224
|
+
// If project ID provided directly, use it
|
|
225
|
+
if (options.projectId) {
|
|
226
|
+
const found = projects.find((p) => p.id === options.projectId);
|
|
227
|
+
if (!found) {
|
|
228
|
+
console.log(`Project with ID "${options.projectId}" not found.`);
|
|
229
|
+
console.log('Available projects:');
|
|
230
|
+
projects.forEach((p) => console.log(` - ${p.name} (${p.id})`));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
selectedProject = found;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Interactive selection
|
|
237
|
+
// Group projects by tenant
|
|
238
|
+
const byTenant = projects.reduce((acc, p) => {
|
|
239
|
+
const key = tenantNameMap.get(p.tenantId) ?? 'Unknown';
|
|
240
|
+
if (!acc[key])
|
|
241
|
+
acc[key] = [];
|
|
242
|
+
acc[key].push(p);
|
|
243
|
+
return acc;
|
|
244
|
+
}, {});
|
|
245
|
+
const choices = Object.entries(byTenant).flatMap(([tenantName, tenantProjects]) => [
|
|
246
|
+
new inquirer.Separator(`── ${tenantName} ──`),
|
|
247
|
+
...tenantProjects.map((p) => ({
|
|
248
|
+
name: ` ${p.name}`,
|
|
249
|
+
value: p.id,
|
|
250
|
+
short: p.name,
|
|
251
|
+
})),
|
|
252
|
+
]);
|
|
253
|
+
const { projectId } = await inquirer.prompt([
|
|
254
|
+
{
|
|
255
|
+
type: 'list',
|
|
256
|
+
name: 'projectId',
|
|
257
|
+
message: 'Select a project:',
|
|
258
|
+
choices,
|
|
259
|
+
},
|
|
260
|
+
]);
|
|
261
|
+
selectedProject = projects.find((p) => p.id === projectId);
|
|
262
|
+
}
|
|
263
|
+
// Provision API key for CLI access
|
|
264
|
+
console.log('\nProvisioning API key for CLI access...');
|
|
265
|
+
const { apiKey, apiKeyId } = await provisionApiKey(options.apiUrl, credentials.accessToken, selectedProject.id, selectedProject.tenantId);
|
|
266
|
+
await updateCredentials({
|
|
267
|
+
selectedProject: {
|
|
268
|
+
id: selectedProject.id,
|
|
269
|
+
name: selectedProject.name,
|
|
270
|
+
tenantId: selectedProject.tenantId,
|
|
271
|
+
apiKey,
|
|
272
|
+
apiKeyId,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
console.log(`\n✓ Selected project: ${selectedProject.name}`);
|
|
276
|
+
console.log('✓ API key provisioned for CLI access');
|
|
277
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AA4BA;;GAEG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAiC3C"}
|