@eide/foir-cli 0.1.33 → 0.1.35
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/dist/cli.js +10 -47
- package/dist/codegen/field-mapping.d.ts.map +1 -1
- package/dist/codegen/field-mapping.js +2 -0
- package/dist/codegen/generators/documents.d.ts.map +1 -1
- package/dist/codegen/generators/documents.js +12 -4
- package/dist/codegen/generators/model-types.js +2 -1
- package/dist/codegen/generators/typed-operations-common.d.ts.map +1 -1
- package/dist/codegen/generators/typed-operations-common.js +9 -1
- package/dist/codegen/generators/typed-operations.d.ts.map +1 -1
- package/dist/codegen/generators/typed-operations.js +17 -6
- package/dist/codegen/swift-field-mapping.d.ts.map +1 -1
- package/dist/codegen/swift-field-mapping.js +6 -0
- package/dist/commands/playground.d.ts +4 -0
- package/dist/commands/playground.d.ts.map +1 -0
- package/dist/commands/playground.js +270 -0
- package/dist/commands/register-commands.d.ts +7 -0
- package/dist/commands/register-commands.d.ts.map +1 -0
- package/dist/commands/register-commands.js +259 -0
- package/dist/commands/select-project.js +2 -2
- package/dist/graphql/generated.d.ts +50 -2298
- package/dist/graphql/generated.d.ts.map +1 -1
- package/dist/graphql/generated.js +180 -9992
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -9,37 +9,18 @@ import { createRequire } from 'module';
|
|
|
9
9
|
import { Command } from 'commander';
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
const { version } = require('../package.json');
|
|
12
|
-
// Auth commands
|
|
12
|
+
// Auth commands (special — not GraphQL-driven)
|
|
13
13
|
import { registerLoginCommand } from './commands/login.js';
|
|
14
14
|
import { registerLogoutCommand } from './commands/logout.js';
|
|
15
15
|
import { registerSelectProjectCommand } from './commands/select-project.js';
|
|
16
16
|
import { registerWhoamiCommand } from './commands/whoami.js';
|
|
17
|
-
//
|
|
18
|
-
import { registerRecordsCommands } from './commands/records.js';
|
|
19
|
-
import { registerModelsCommands } from './commands/models.js';
|
|
20
|
-
import { registerSearchCommands } from './commands/search.js';
|
|
21
|
-
import { registerCustomersCommands } from './commands/customers.js';
|
|
22
|
-
import { registerCustomerProfilesCommands } from './commands/customer-profiles.js';
|
|
23
|
-
import { registerSegmentsCommands } from './commands/segments.js';
|
|
24
|
-
import { registerExperimentsCommands } from './commands/experiments.js';
|
|
25
|
-
import { registerSettingsCommands } from './commands/settings.js';
|
|
26
|
-
import { registerApiKeysCommands } from './commands/api-keys.js';
|
|
27
|
-
import { registerAuthProvidersCommands } from './commands/auth-providers.js';
|
|
28
|
-
import { registerExtensionsCommands } from './commands/extensions.js';
|
|
29
|
-
import { registerOperationsCommands } from './commands/operations.js';
|
|
30
|
-
import { registerHooksCommands } from './commands/hooks.js';
|
|
31
|
-
import { registerSchedulesCommands } from './commands/schedules.js';
|
|
17
|
+
// Special commands (REST, codegen, scaffolding, multi-type search)
|
|
32
18
|
import { registerMediaCommands } from './commands/media.js';
|
|
33
|
-
import { registerContextCommands } from './commands/context.js';
|
|
34
|
-
import { registerNotificationsCommands } from './commands/notifications.js';
|
|
35
|
-
import { registerLocalesCommands } from './commands/locales.js';
|
|
36
|
-
import { registerFilesCommands } from './commands/files.js';
|
|
37
|
-
import { registerNotesCommands } from './commands/notes.js';
|
|
38
|
-
import { registerVariantCatalogCommands } from './commands/variant-catalog.js';
|
|
39
|
-
import { registerAuthConfigCommands } from './commands/auth-config.js';
|
|
40
|
-
import { registerEmbeddingsCommands } from './commands/embeddings.js';
|
|
41
19
|
import { registerPullCommand } from './commands/pull.js';
|
|
42
20
|
import { registerCreateExtensionCommand } from './commands/create-extension.js';
|
|
21
|
+
import { registerSearchCommands } from './commands/search.js';
|
|
22
|
+
// Dynamic commands from command registry (replaces ~25 imperative command files)
|
|
23
|
+
import { registerDynamicCommands } from './commands/register-commands.js';
|
|
43
24
|
const program = new Command();
|
|
44
25
|
program
|
|
45
26
|
.name('foir')
|
|
@@ -58,36 +39,18 @@ function getGlobalOpts() {
|
|
|
58
39
|
quiet: !!opts.quiet,
|
|
59
40
|
};
|
|
60
41
|
}
|
|
61
|
-
//
|
|
42
|
+
// Auth commands
|
|
62
43
|
registerLoginCommand(program, getGlobalOpts);
|
|
63
44
|
registerLogoutCommand(program, getGlobalOpts);
|
|
64
45
|
registerSelectProjectCommand(program, getGlobalOpts);
|
|
65
46
|
registerWhoamiCommand(program, getGlobalOpts);
|
|
66
|
-
|
|
67
|
-
registerModelsCommands(program, getGlobalOpts);
|
|
68
|
-
registerSearchCommands(program, getGlobalOpts);
|
|
69
|
-
registerCustomersCommands(program, getGlobalOpts);
|
|
70
|
-
registerCustomerProfilesCommands(program, getGlobalOpts);
|
|
71
|
-
registerSegmentsCommands(program, getGlobalOpts);
|
|
72
|
-
registerExperimentsCommands(program, getGlobalOpts);
|
|
73
|
-
registerSettingsCommands(program, getGlobalOpts);
|
|
74
|
-
registerApiKeysCommands(program, getGlobalOpts);
|
|
75
|
-
registerAuthProvidersCommands(program, getGlobalOpts);
|
|
76
|
-
registerExtensionsCommands(program, getGlobalOpts);
|
|
77
|
-
registerOperationsCommands(program, getGlobalOpts);
|
|
78
|
-
registerHooksCommands(program, getGlobalOpts);
|
|
79
|
-
registerSchedulesCommands(program, getGlobalOpts);
|
|
47
|
+
// Special commands
|
|
80
48
|
registerMediaCommands(program, getGlobalOpts);
|
|
81
|
-
|
|
82
|
-
registerNotificationsCommands(program, getGlobalOpts);
|
|
83
|
-
registerLocalesCommands(program, getGlobalOpts);
|
|
84
|
-
registerFilesCommands(program, getGlobalOpts);
|
|
85
|
-
registerNotesCommands(program, getGlobalOpts);
|
|
86
|
-
registerVariantCatalogCommands(program, getGlobalOpts);
|
|
87
|
-
registerAuthConfigCommands(program, getGlobalOpts);
|
|
88
|
-
registerEmbeddingsCommands(program, getGlobalOpts);
|
|
49
|
+
registerSearchCommands(program, getGlobalOpts);
|
|
89
50
|
// Codegen
|
|
90
51
|
registerPullCommand(program, getGlobalOpts);
|
|
91
52
|
// Scaffolding
|
|
92
53
|
registerCreateExtensionCommand(program, getGlobalOpts);
|
|
54
|
+
// All GraphQL-driven commands (models, records, locales, etc.)
|
|
55
|
+
registerDynamicCommands(program, getGlobalOpts);
|
|
93
56
|
program.parse();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/field-mapping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/field-mapping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,aAqBhC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAqD1D,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,QAAQ,GAAG,OAAkB,GAClC,MAAM,CAqCR;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAW3E;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EAAE,GAC1B,GAAG,CAAC,MAAM,CAAC,CAUb;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,iBAAiB,EAAE,GAC1B,GAAG,CAAC,MAAM,CAAC,CAcb;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CA2EjE"}
|
|
@@ -22,6 +22,7 @@ export const PRIMITIVE_FIELD_TYPES = new Set([
|
|
|
22
22
|
'reference',
|
|
23
23
|
'link',
|
|
24
24
|
'flexible',
|
|
25
|
+
'model',
|
|
25
26
|
]);
|
|
26
27
|
export function isPrimitiveFieldType(type) {
|
|
27
28
|
return PRIMITIVE_FIELD_TYPES.has(type);
|
|
@@ -78,6 +79,7 @@ export const FIELD_TYPE_MAPPING = {
|
|
|
78
79
|
inputType: 'LinkValue',
|
|
79
80
|
needsImport: 'field-types',
|
|
80
81
|
},
|
|
82
|
+
model: { outputType: 'string', inputType: 'string' },
|
|
81
83
|
};
|
|
82
84
|
export function getFieldType(field, mode = 'output') {
|
|
83
85
|
if (!field?.type)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/documents.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/documents.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAyGlE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAoBhD"}
|
|
@@ -25,10 +25,10 @@ fragment ${typeName}Fields on Record {
|
|
|
25
25
|
updatedAt
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
|
|
28
|
+
query Get${typeName}($id: ID!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
29
29
|
record(id: $id) {
|
|
30
30
|
...${typeName}Fields
|
|
31
|
-
resolved(locale: $locale, preview: $preview) {
|
|
31
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
32
32
|
content
|
|
33
33
|
record { id modelKey naturalKey }
|
|
34
34
|
version { id versionNumber }
|
|
@@ -36,10 +36,10 @@ query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean) {
|
|
39
|
+
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
40
40
|
recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
|
|
41
41
|
...${typeName}Fields
|
|
42
|
-
resolved(locale: $locale, preview: $preview) {
|
|
42
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
43
43
|
content
|
|
44
44
|
record { id modelKey naturalKey }
|
|
45
45
|
version { id versionNumber }
|
|
@@ -52,6 +52,9 @@ query List${pluralName}(
|
|
|
52
52
|
$offset: Int
|
|
53
53
|
$filters: [FilterInput!]
|
|
54
54
|
$sort: SortInput
|
|
55
|
+
$locale: String
|
|
56
|
+
$preview: Boolean
|
|
57
|
+
$fields: FieldSelectionInput
|
|
55
58
|
) {
|
|
56
59
|
records(
|
|
57
60
|
modelKey: "${model.key}"
|
|
@@ -62,6 +65,11 @@ query List${pluralName}(
|
|
|
62
65
|
) {
|
|
63
66
|
items {
|
|
64
67
|
...${typeName}Fields
|
|
68
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
69
|
+
content
|
|
70
|
+
record { id modelKey naturalKey }
|
|
71
|
+
version { id versionNumber }
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
total
|
|
67
75
|
}
|
|
@@ -147,7 +147,8 @@ function generateDataInterface(model, fields, interfaceName, allModels) {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
// Only parameterize if all targets resolved — otherwise keep default generic
|
|
150
|
-
if (resolvedPreviewTypes.length === refTypes.length &&
|
|
150
|
+
if (resolvedPreviewTypes.length === refTypes.length &&
|
|
151
|
+
resolvedPreviewTypes.length > 0) {
|
|
151
152
|
fieldType = `ReferenceValue<${resolvedPreviewTypes.join(' | ')}>`;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-operations-common.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations-common.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"typed-operations-common.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations-common.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CA+E1E"}
|
|
@@ -35,7 +35,7 @@ export interface ResolvedContent<T> {
|
|
|
35
35
|
|
|
36
36
|
/** Paginated list result. */
|
|
37
37
|
export interface PaginatedResult<T> {
|
|
38
|
-
items: BaseRecord<T>[];
|
|
38
|
+
items: (BaseRecord<T> & { resolved: ResolvedContent<T> | null })[];
|
|
39
39
|
total: number;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -72,5 +72,13 @@ export interface ShareResult {
|
|
|
72
72
|
export interface ShareWithRecord<T> extends ShareResult {
|
|
73
73
|
record: BaseRecord<T>;
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
/** Field selection for resolved content — pick or omit specific fields. */
|
|
77
|
+
export interface FieldSelection<T = Record<string, unknown>> {
|
|
78
|
+
/** Include only these field keys (mutually exclusive with omit) */
|
|
79
|
+
pick?: (keyof T & string)[];
|
|
80
|
+
/** Exclude these field keys (mutually exclusive with omit) */
|
|
81
|
+
omit?: (keyof T & string)[];
|
|
82
|
+
}
|
|
75
83
|
`;
|
|
76
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-operations.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,
|
|
1
|
+
{"version":3,"file":"typed-operations.d.ts","sourceRoot":"","sources":["../../../src/codegen/generators/typed-operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,YAAY,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CAwRR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5E"}
|
|
@@ -29,7 +29,8 @@ import type {
|
|
|
29
29
|
PaginatedResult,
|
|
30
30
|
CreateRecordResult,
|
|
31
31
|
UpdateRecordResult,
|
|
32
|
-
DeleteRecordResult
|
|
32
|
+
DeleteRecordResult,
|
|
33
|
+
FieldSelection,${model.config.sharing?.enabled ? '\n ShareResult,\n ShareWithRecord,' : ''}
|
|
33
34
|
} from './_common.js';
|
|
34
35
|
`);
|
|
35
36
|
// Type alias
|
|
@@ -37,12 +38,12 @@ import type {
|
|
|
37
38
|
lines.push('');
|
|
38
39
|
// GET query
|
|
39
40
|
lines.push(`export const GET_${upperSnake} = \`
|
|
40
|
-
query Get${typeName}($id: ID!, $locale: String, $preview: Boolean) {
|
|
41
|
+
query Get${typeName}($id: ID!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
41
42
|
record(id: $id) {
|
|
42
43
|
id modelKey naturalKey data metadata
|
|
43
44
|
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
44
45
|
createdAt updatedAt
|
|
45
|
-
resolved(locale: $locale, preview: $preview) {
|
|
46
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
46
47
|
content
|
|
47
48
|
record { id modelKey naturalKey }
|
|
48
49
|
version { id versionNumber }
|
|
@@ -55,6 +56,7 @@ import type {
|
|
|
55
56
|
id: string;
|
|
56
57
|
locale?: string;
|
|
57
58
|
preview?: boolean;
|
|
59
|
+
fields?: FieldSelection<${dataType}>;
|
|
58
60
|
}`);
|
|
59
61
|
lines.push('');
|
|
60
62
|
lines.push(`export interface Get${typeName}Result {
|
|
@@ -65,12 +67,12 @@ import type {
|
|
|
65
67
|
lines.push('');
|
|
66
68
|
// GET BY KEY query
|
|
67
69
|
lines.push(`export const GET_${upperSnake}_BY_KEY = \`
|
|
68
|
-
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean) {
|
|
70
|
+
query Get${typeName}ByKey($naturalKey: String!, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
69
71
|
recordByKey(modelKey: "${model.key}", naturalKey: $naturalKey) {
|
|
70
72
|
id modelKey naturalKey data metadata
|
|
71
73
|
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
72
74
|
createdAt updatedAt
|
|
73
|
-
resolved(locale: $locale, preview: $preview) {
|
|
75
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
74
76
|
content
|
|
75
77
|
record { id modelKey naturalKey }
|
|
76
78
|
version { id versionNumber }
|
|
@@ -83,6 +85,7 @@ import type {
|
|
|
83
85
|
naturalKey: string;
|
|
84
86
|
locale?: string;
|
|
85
87
|
preview?: boolean;
|
|
88
|
+
fields?: FieldSelection<${dataType}>;
|
|
86
89
|
}`);
|
|
87
90
|
lines.push('');
|
|
88
91
|
lines.push(`export interface Get${typeName}ByKeyResult {
|
|
@@ -93,12 +96,17 @@ import type {
|
|
|
93
96
|
lines.push('');
|
|
94
97
|
// LIST query
|
|
95
98
|
lines.push(`export const LIST_${pluralUpperSnake} = \`
|
|
96
|
-
query List${pluralName}($limit: Int, $offset: Int, $filters: [FilterInput!], $sort: SortInput) {
|
|
99
|
+
query List${pluralName}($limit: Int, $offset: Int, $filters: [FilterInput!], $sort: SortInput, $locale: String, $preview: Boolean, $fields: FieldSelectionInput) {
|
|
97
100
|
records(modelKey: "${model.key}", limit: $limit, offset: $offset, filters: $filters, sort: $sort) {
|
|
98
101
|
items {
|
|
99
102
|
id modelKey naturalKey data metadata
|
|
100
103
|
publishedVersionNumber publishedAt versionNumber changeDescription
|
|
101
104
|
createdAt updatedAt
|
|
105
|
+
resolved(locale: $locale, preview: $preview, fields: $fields) {
|
|
106
|
+
content
|
|
107
|
+
record { id modelKey naturalKey }
|
|
108
|
+
version { id versionNumber }
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
111
|
total
|
|
104
112
|
}
|
|
@@ -110,6 +118,9 @@ import type {
|
|
|
110
118
|
offset?: number;
|
|
111
119
|
filters?: Array<{ field: string; operator: string; value: JsonValue }>;
|
|
112
120
|
sort?: { field: string; direction: 'ASC' | 'DESC' };
|
|
121
|
+
locale?: string;
|
|
122
|
+
preview?: boolean;
|
|
123
|
+
fields?: FieldSelection<${dataType}>;
|
|
113
124
|
}`);
|
|
114
125
|
lines.push('');
|
|
115
126
|
lines.push(`export interface List${pluralName}Result {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swift-field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/swift-field-mapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"swift-field-mapping.d.ts","sourceRoot":"","sources":["../../src/codegen/swift-field-mapping.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA8HrE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC,CAeA"}
|
|
@@ -124,6 +124,12 @@ export const SWIFT_FIELD_TYPE_MAPPING = {
|
|
|
124
124
|
castExpression: 'as? [String: Any]',
|
|
125
125
|
needsSharedType: true,
|
|
126
126
|
},
|
|
127
|
+
model: {
|
|
128
|
+
type: 'String',
|
|
129
|
+
alwaysOptional: true,
|
|
130
|
+
defaultValue: '""',
|
|
131
|
+
castExpression: 'as? String',
|
|
132
|
+
},
|
|
127
133
|
};
|
|
128
134
|
/**
|
|
129
135
|
* Get the Swift type for a field.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../src/commands/playground.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAgCtD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,MAAM,aAAa,QA8QnC"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { createClient } from '../lib/client.js';
|
|
7
|
+
import { getProjectContext } from '../auth/credentials.js';
|
|
8
|
+
import { ListModelsDocument } from '../graphql/generated.js';
|
|
9
|
+
const HISTORY_FILE = join(homedir(), '.foir', 'repl-history');
|
|
10
|
+
const MAX_HISTORY = 500;
|
|
11
|
+
function loadHistory() {
|
|
12
|
+
try {
|
|
13
|
+
if (existsSync(HISTORY_FILE)) {
|
|
14
|
+
return readFileSync(HISTORY_FILE, 'utf-8')
|
|
15
|
+
.split('\n')
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.slice(-MAX_HISTORY);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// ignore
|
|
22
|
+
}
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
function saveHistory(lines) {
|
|
26
|
+
try {
|
|
27
|
+
const dir = join(homedir(), '.foir');
|
|
28
|
+
if (!existsSync(dir))
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
writeFileSync(HISTORY_FILE, lines.slice(-MAX_HISTORY).join('\n') + '\n');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function registerPlaygroundCommand(program, getGlobalOpts) {
|
|
37
|
+
program
|
|
38
|
+
.command('playground')
|
|
39
|
+
.alias('repl')
|
|
40
|
+
.description('Interactive REPL with tab completion')
|
|
41
|
+
.action(async () => {
|
|
42
|
+
const opts = getGlobalOpts();
|
|
43
|
+
// Validate auth and create client
|
|
44
|
+
let client;
|
|
45
|
+
try {
|
|
46
|
+
client = await createClient(opts);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new Error('Authentication required. Run `foir login` first.');
|
|
50
|
+
}
|
|
51
|
+
// Pre-fetch model keys for tab completion (don't block startup)
|
|
52
|
+
let modelKeys = [];
|
|
53
|
+
client
|
|
54
|
+
.request(ListModelsDocument, { limit: 200 })
|
|
55
|
+
.then((data) => {
|
|
56
|
+
modelKeys = data.models.items.map((m) => m.key);
|
|
57
|
+
})
|
|
58
|
+
.catch(() => {
|
|
59
|
+
/* ignore — completion just won't have model keys */
|
|
60
|
+
});
|
|
61
|
+
// Get project context for prompt
|
|
62
|
+
let projectName = 'no project';
|
|
63
|
+
try {
|
|
64
|
+
const ctx = await getProjectContext();
|
|
65
|
+
if (ctx?.name)
|
|
66
|
+
projectName = ctx.name;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// no project context
|
|
70
|
+
}
|
|
71
|
+
// Collect command names for completion
|
|
72
|
+
const commandNames = program.commands.map((c) => c.name());
|
|
73
|
+
const commandMap = new Map();
|
|
74
|
+
for (const cmd of program.commands) {
|
|
75
|
+
commandMap.set(cmd.name(), cmd);
|
|
76
|
+
for (const alias of cmd.aliases()) {
|
|
77
|
+
commandMap.set(alias, cmd);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Build completer
|
|
81
|
+
const metaCommands = [
|
|
82
|
+
'.help',
|
|
83
|
+
'.clear',
|
|
84
|
+
'.exit',
|
|
85
|
+
'.commands',
|
|
86
|
+
'.context',
|
|
87
|
+
'.models',
|
|
88
|
+
];
|
|
89
|
+
function completer(line) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
const parts = trimmed.split(/\s+/);
|
|
92
|
+
// Meta commands
|
|
93
|
+
if (parts.length === 1 && trimmed.startsWith('.')) {
|
|
94
|
+
const hits = metaCommands.filter((c) => c.startsWith(trimmed));
|
|
95
|
+
return [hits.length ? hits : metaCommands, trimmed];
|
|
96
|
+
}
|
|
97
|
+
// First word: command names
|
|
98
|
+
if (parts.length <= 1) {
|
|
99
|
+
const hits = commandNames.filter((c) => c.startsWith(trimmed));
|
|
100
|
+
return [hits.length ? hits : commandNames, trimmed];
|
|
101
|
+
}
|
|
102
|
+
// Second word: subcommands
|
|
103
|
+
const firstPart = parts[0] ?? '';
|
|
104
|
+
const cmd = commandMap.get(firstPart);
|
|
105
|
+
if (cmd && parts.length === 2) {
|
|
106
|
+
const subNames = cmd.commands.map((c) => c.name());
|
|
107
|
+
const partial = parts[1] ?? '';
|
|
108
|
+
const hits = subNames.filter((s) => s.startsWith(partial));
|
|
109
|
+
return [hits.length ? hits : subNames, partial];
|
|
110
|
+
}
|
|
111
|
+
// Level 3: model keys for records/search commands
|
|
112
|
+
const cmdName = cmd?.name();
|
|
113
|
+
if ((cmdName === 'records' || cmdName === 'search') &&
|
|
114
|
+
parts.length === 3 &&
|
|
115
|
+
modelKeys.length > 0) {
|
|
116
|
+
const partial = parts[2] ?? '';
|
|
117
|
+
const hits = modelKeys.filter((k) => k.startsWith(partial));
|
|
118
|
+
return [hits.length ? hits : modelKeys, partial];
|
|
119
|
+
}
|
|
120
|
+
// Flags
|
|
121
|
+
if (cmd) {
|
|
122
|
+
const lastPart = parts[parts.length - 1] ?? '';
|
|
123
|
+
if (lastPart.startsWith('-')) {
|
|
124
|
+
const flags = cmd.options.map((o) => o.long).filter((f) => Boolean(f));
|
|
125
|
+
const hits = flags.filter((f) => f.startsWith(lastPart));
|
|
126
|
+
return [hits.length ? hits : [], lastPart];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return [[], trimmed];
|
|
130
|
+
}
|
|
131
|
+
// Load history
|
|
132
|
+
const history = loadHistory();
|
|
133
|
+
const rl = readline.createInterface({
|
|
134
|
+
input: process.stdin,
|
|
135
|
+
output: process.stdout,
|
|
136
|
+
prompt: chalk.cyan(`foir (${projectName})> `),
|
|
137
|
+
completer,
|
|
138
|
+
history,
|
|
139
|
+
historySize: MAX_HISTORY,
|
|
140
|
+
});
|
|
141
|
+
console.log(chalk.bold('\nFoir Interactive Playground'));
|
|
142
|
+
console.log(chalk.dim('Type commands without the "foir" prefix. Use Tab for completion.'));
|
|
143
|
+
console.log(chalk.dim('Type .help for available meta-commands.\n'));
|
|
144
|
+
rl.prompt();
|
|
145
|
+
// Prevent Commander from calling process.exit()
|
|
146
|
+
program.exitOverride();
|
|
147
|
+
rl.on('line', async (line) => {
|
|
148
|
+
const input = line.trim();
|
|
149
|
+
if (!input) {
|
|
150
|
+
rl.prompt();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Meta commands
|
|
154
|
+
if (input === '.help') {
|
|
155
|
+
console.log(chalk.bold('\nMeta-commands:'));
|
|
156
|
+
console.log(' .help Show this help');
|
|
157
|
+
console.log(' .clear Clear terminal');
|
|
158
|
+
console.log(' .exit Exit playground');
|
|
159
|
+
console.log(' .commands List available commands');
|
|
160
|
+
console.log(' .context Show current auth/project context');
|
|
161
|
+
console.log(' .models Refresh and list model keys');
|
|
162
|
+
console.log(chalk.dim('\nRun any foir command without the "foir" prefix.'));
|
|
163
|
+
console.log(chalk.dim('Example: records list page\n'));
|
|
164
|
+
rl.prompt();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (input === '.clear') {
|
|
168
|
+
console.clear();
|
|
169
|
+
rl.prompt();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (input === '.exit' || input === 'exit' || input === 'quit') {
|
|
173
|
+
saveHistory(rl.history || []);
|
|
174
|
+
console.log(chalk.dim('Goodbye!'));
|
|
175
|
+
rl.close();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (input === '.commands') {
|
|
179
|
+
console.log(chalk.bold('\nAvailable commands:'));
|
|
180
|
+
for (const cmd of program.commands) {
|
|
181
|
+
if (cmd.name() === 'playground')
|
|
182
|
+
continue;
|
|
183
|
+
const desc = cmd.description() || '';
|
|
184
|
+
console.log(` ${chalk.green(cmd.name().padEnd(24))} ${chalk.dim(desc)}`);
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
rl.prompt();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (input === '.context') {
|
|
191
|
+
try {
|
|
192
|
+
const ctx = await getProjectContext();
|
|
193
|
+
console.log(chalk.bold('\nCurrent context:'));
|
|
194
|
+
if (ctx) {
|
|
195
|
+
console.log(` Project: ${chalk.green(ctx.name || 'unnamed')}`);
|
|
196
|
+
console.log(` ID: ${chalk.dim(ctx.id || 'n/a')}`);
|
|
197
|
+
console.log(` Tenant: ${chalk.dim(ctx.tenantId || 'n/a')}`);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.log(chalk.yellow(' No project selected. Run: select-project'));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
console.log(chalk.yellow(' No project context available.'));
|
|
205
|
+
}
|
|
206
|
+
console.log('');
|
|
207
|
+
rl.prompt();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (input === '.models') {
|
|
211
|
+
try {
|
|
212
|
+
const data = await client.request(ListModelsDocument, {
|
|
213
|
+
limit: 200,
|
|
214
|
+
});
|
|
215
|
+
modelKeys = data.models.items.map((m) => m.key);
|
|
216
|
+
console.log(chalk.bold('\nModel keys:'));
|
|
217
|
+
for (const key of modelKeys) {
|
|
218
|
+
console.log(` ${chalk.green(key)}`);
|
|
219
|
+
}
|
|
220
|
+
if (modelKeys.length === 0) {
|
|
221
|
+
console.log(chalk.yellow(' No models found.'));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
console.log(chalk.yellow(' Failed to fetch models.'));
|
|
226
|
+
}
|
|
227
|
+
console.log('');
|
|
228
|
+
rl.prompt();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// Regular command - parse and execute via Commander
|
|
232
|
+
const args = input.match(/(?:[^\s"']+|"[^"]*"|'[^']*')/g) || [];
|
|
233
|
+
// Strip quotes from arguments
|
|
234
|
+
const cleanArgs = args.map((a) => a.replace(/^["']|["']$/g, ''));
|
|
235
|
+
try {
|
|
236
|
+
await program.parseAsync(['node', 'foir', ...cleanArgs], {
|
|
237
|
+
from: 'user',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
// CommanderError is thrown by exitOverride instead of process.exit
|
|
242
|
+
if (err.code === 'commander.helpDisplayed' ||
|
|
243
|
+
err.code === 'commander.version') {
|
|
244
|
+
// Help/version output already printed
|
|
245
|
+
}
|
|
246
|
+
else if (err.code === 'commander.unknownCommand') {
|
|
247
|
+
console.error(chalk.red(`Unknown command: ${cleanArgs[0]}`));
|
|
248
|
+
console.log(chalk.dim('Type .commands to see available commands.'));
|
|
249
|
+
}
|
|
250
|
+
else if (err.exitCode !== undefined) {
|
|
251
|
+
// Commander error with exit code - already printed message
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// Application error
|
|
255
|
+
console.error(chalk.red(err.message || String(err)));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
console.log(''); // spacing
|
|
259
|
+
rl.prompt();
|
|
260
|
+
});
|
|
261
|
+
rl.on('close', () => {
|
|
262
|
+
saveHistory(rl.history || []);
|
|
263
|
+
});
|
|
264
|
+
// Handle SIGINT gracefully
|
|
265
|
+
rl.on('SIGINT', () => {
|
|
266
|
+
console.log(chalk.dim('\n(Use .exit or Ctrl+D to quit)'));
|
|
267
|
+
rl.prompt();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { GlobalOptions } from '../lib/config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register all declarative commands from the command registry.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerDynamicCommands(program: Command, globalOpts: () => GlobalOptions): void;
|
|
7
|
+
//# sourceMappingURL=register-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-commands.d.ts","sourceRoot":"","sources":["../../src/commands/register-commands.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA0GtD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,aAAa,GAC9B,IAAI,CA8LN"}
|