@factorialco/fcode-cli 2.0.0 → 2.2.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 +248 -2
- package/dist/commands/add.js +3 -1
- package/dist/commands/clone.d.ts +1 -0
- package/dist/commands/clone.js +5 -0
- package/dist/commands/i18n/add.d.ts +9 -0
- package/dist/commands/i18n/add.js +13 -0
- package/dist/commands/i18n/pull.d.ts +12 -0
- package/dist/commands/i18n/pull.js +14 -0
- package/dist/commands/i18n/push.d.ts +12 -0
- package/dist/commands/i18n/push.js +14 -0
- package/dist/commands/i18n/remove.d.ts +9 -0
- package/dist/commands/i18n/remove.js +19 -0
- package/dist/commands/i18n/reset.d.ts +9 -0
- package/dist/commands/i18n/reset.js +13 -0
- package/dist/commands/i18n/status.d.ts +11 -0
- package/dist/commands/i18n/status.js +42 -0
- package/dist/commands/pull.js +5 -3
- package/dist/commands/push.js +5 -3
- package/dist/commands/reset.js +3 -1
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +5 -2
- package/dist/commands/setup-debug.js +2 -2
- package/dist/commands/status.js +4 -2
- package/dist/commands/team/aliases/delete.d.ts +9 -0
- package/dist/commands/team/aliases/delete.js +28 -0
- package/dist/commands/team/aliases/list.d.ts +6 -0
- package/dist/commands/team/aliases/list.js +23 -0
- package/dist/commands/team/aliases/set.d.ts +10 -0
- package/dist/commands/team/aliases/set.js +25 -0
- package/dist/commands/team/pull.js +8 -1
- package/dist/commands/team/push.js +6 -0
- package/dist/commands/team/versions/create.d.ts +13 -0
- package/dist/commands/team/versions/create.js +42 -0
- package/dist/commands/team/versions/delete.d.ts +12 -0
- package/dist/commands/team/versions/delete.js +32 -0
- package/dist/commands/team/versions/list.d.ts +6 -0
- package/dist/commands/team/versions/list.js +25 -0
- package/dist/commands/variables/status.js +11 -0
- package/dist/config/remote.js +1 -1
- package/dist/config/settings.d.ts +1 -0
- package/dist/config/settings.js +6 -0
- package/dist/config/workspace.js +10 -0
- package/dist/core/commands/i18n-command.d.ts +5 -0
- package/dist/core/commands/i18n-command.js +16 -0
- package/dist/core/errors.d.ts +6 -0
- package/dist/core/errors.js +19 -1
- package/dist/core/executors/execution-task.js +4 -0
- package/dist/core/executors/javascript-execution-task.js +26 -1
- package/dist/core/resource-pusher.js +15 -0
- package/dist/core/types.d.ts +66 -2
- package/dist/core/types.js +45 -3
- package/dist/env.d.ts +2 -2
- package/dist/env.js +1 -1
- package/dist/repositories/locale.d.ts +24 -0
- package/dist/repositories/locale.js +102 -0
- package/dist/repositories/resource.js +4 -1
- package/dist/repositories/team.d.ts +7 -1
- package/dist/repositories/team.js +39 -2
- package/dist/repositories/variable.d.ts +24 -0
- package/dist/repositories/variable.js +70 -22
- package/dist/runtime/executor.js +2 -2
- package/dist/runtime/executor.py +2 -2
- package/dist/runtime/libs/python/fcode/__init__.py +3 -2
- package/dist/runtime/libs/python/fcode/context.py +43 -1
- package/dist/services/fcode-graphql-client.d.ts +20 -1
- package/dist/services/fcode-graphql-client.js +276 -17
- package/dist/services/locale-merger.d.ts +23 -0
- package/dist/services/locale-merger.js +111 -0
- package/dist/services/locale-resolver.d.ts +18 -0
- package/dist/services/locale-resolver.js +120 -0
- package/dist/services/process-runner.d.ts +1 -0
- package/dist/services/process-runner.js +2 -2
- package/dist/utils/gitignore.d.ts +2 -0
- package/dist/utils/gitignore.js +9 -0
- package/oclif.manifest.json +511 -127
- package/package.json +11 -2
package/dist/commands/status.js
CHANGED
|
@@ -4,8 +4,9 @@ const base_command_1 = require("../core/commands/base-command");
|
|
|
4
4
|
const status_1 = require("./modules/status");
|
|
5
5
|
const status_2 = require("./processes/status");
|
|
6
6
|
const status_3 = require("./variables/status");
|
|
7
|
-
const status_4 = require("./
|
|
8
|
-
const status_5 = require("./
|
|
7
|
+
const status_4 = require("./i18n/status");
|
|
8
|
+
const status_5 = require("./dependencies/status");
|
|
9
|
+
const status_6 = require("./team/status");
|
|
9
10
|
class Status extends base_command_1.default {
|
|
10
11
|
async run() {
|
|
11
12
|
await this.parse(Status);
|
|
@@ -15,6 +16,7 @@ class Status extends base_command_1.default {
|
|
|
15
16
|
await status_3.default.run([]);
|
|
16
17
|
await status_4.default.run([]);
|
|
17
18
|
await status_5.default.run([]);
|
|
19
|
+
await status_6.default.run([]);
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
Status.description = 'Show team workspace status';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ApiClientCommand from '../../../core/commands/api-client-command';
|
|
2
|
+
export default class TeamAliasesDeleteCommand extends ApiClientCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const color_1 = require("@oclif/color");
|
|
5
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
6
|
+
const pull_1 = require("../pull");
|
|
7
|
+
const cli_1 = require("../../../utils/cli");
|
|
8
|
+
class TeamAliasesDeleteCommand extends api_client_command_1.default {
|
|
9
|
+
async run() {
|
|
10
|
+
const { args: { name }, } = await this.parse(TeamAliasesDeleteCommand);
|
|
11
|
+
const aliases = await this._fcodeGraphQLClient.allTeamVersionAliases();
|
|
12
|
+
const alias = aliases.find((candidate) => candidate.name === name);
|
|
13
|
+
if (!alias) {
|
|
14
|
+
cli_1.default.error(`Alias ${name} does not exist`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
cli_1.default.log(color_1.default.cyan(`\n🗑️ deleting alias ${name}...`));
|
|
18
|
+
await this._fcodeGraphQLClient.deleteTeamVersionAlias(alias.id);
|
|
19
|
+
cli_1.default.log(color_1.default.green(`✅ alias ${name} deleted`));
|
|
20
|
+
await pull_1.default.run([]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
TeamAliasesDeleteCommand.description = 'Delete a team version alias and the per-entity aliases carrying its name on every owned process and module';
|
|
24
|
+
TeamAliasesDeleteCommand.examples = ['$ fcode team:aliases:delete production'];
|
|
25
|
+
TeamAliasesDeleteCommand.args = {
|
|
26
|
+
name: core_1.Args.string({ description: 'alias name to delete', required: true }),
|
|
27
|
+
};
|
|
28
|
+
exports.default = TeamAliasesDeleteCommand;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@oclif/color");
|
|
4
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
5
|
+
const cli_1 = require("../../../utils/cli");
|
|
6
|
+
class TeamAliasesListCommand extends api_client_command_1.default {
|
|
7
|
+
async run() {
|
|
8
|
+
await this.parse(TeamAliasesListCommand);
|
|
9
|
+
const aliases = await this._fcodeGraphQLClient.allTeamVersionAliases();
|
|
10
|
+
if (aliases.length === 0) {
|
|
11
|
+
cli_1.default.log('📋 No team version aliases yet.');
|
|
12
|
+
cli_1.default.log(`Create one with ${color_1.default.cyan('fcode team:aliases:set [name] [tag]')}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
cli_1.default.log('🔗 Team version aliases:\n');
|
|
16
|
+
for (const alias of aliases) {
|
|
17
|
+
cli_1.default.log(` ${color_1.default.cyan(alias.name)} → ${alias.tag}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
TeamAliasesListCommand.description = 'List the team version aliases of the active team workspace';
|
|
22
|
+
TeamAliasesListCommand.examples = ['$ fcode team:aliases:list'];
|
|
23
|
+
exports.default = TeamAliasesListCommand;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import ApiClientCommand from '../../../core/commands/api-client-command';
|
|
2
|
+
export default class TeamAliasesSetCommand extends ApiClientCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
tag: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const color_1 = require("@oclif/color");
|
|
5
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
6
|
+
const pull_1 = require("../pull");
|
|
7
|
+
const cli_1 = require("../../../utils/cli");
|
|
8
|
+
class TeamAliasesSetCommand extends api_client_command_1.default {
|
|
9
|
+
async run() {
|
|
10
|
+
const { args: { name, tag }, } = await this.parse(TeamAliasesSetCommand);
|
|
11
|
+
cli_1.default.log(color_1.default.cyan(`\n🔗 pointing alias ${name} at ${tag}...`));
|
|
12
|
+
await this._fcodeGraphQLClient.createTeamVersionAlias(name, tag);
|
|
13
|
+
cli_1.default.log(color_1.default.green(`✅ alias ${name} → ${tag}`));
|
|
14
|
+
// Aliases have no folder representation; refreshing team.json is enough.
|
|
15
|
+
await pull_1.default.run([]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
TeamAliasesSetCommand.description = 'Create or re-point a team version alias, upserting the per-entity alias on every owned ' +
|
|
19
|
+
'process and module that has the target tag published';
|
|
20
|
+
TeamAliasesSetCommand.examples = ['$ fcode team:aliases:set production v1.0.0'];
|
|
21
|
+
TeamAliasesSetCommand.args = {
|
|
22
|
+
name: core_1.Args.string({ description: 'alias name (e.g. production)', required: true }),
|
|
23
|
+
tag: core_1.Args.string({ description: 'team version tag the alias points at', required: true }),
|
|
24
|
+
};
|
|
25
|
+
exports.default = TeamAliasesSetCommand;
|
|
@@ -12,9 +12,16 @@ class TeamPullCommand extends api_client_command_1.default {
|
|
|
12
12
|
async run() {
|
|
13
13
|
await this.parse(TeamPullCommand);
|
|
14
14
|
cli_1.default.log(color_1.default.cyan('\n🗄️ pulling team settings...'));
|
|
15
|
-
const raw = await
|
|
15
|
+
const [raw, versions, aliases] = await Promise.all([
|
|
16
|
+
this._fcodeGraphQLClient.team(),
|
|
17
|
+
this._fcodeGraphQLClient.allTeamVersions(),
|
|
18
|
+
this._fcodeGraphQLClient.allTeamVersionAliases(),
|
|
19
|
+
]);
|
|
16
20
|
const team = await this._teamRepository.fromRaw(raw);
|
|
21
|
+
this._teamRepository.applyCloudVersioning(team, versions, aliases);
|
|
17
22
|
this._teamRepository.save(team);
|
|
23
|
+
// The stored hash intentionally excludes versions/aliases (see Team.contentHash), so
|
|
24
|
+
// pull-only fields never surface as a phantom MODIFIED state.
|
|
18
25
|
await this.workspaceConfig.setTeamStoredHash(team.contentHash);
|
|
19
26
|
}
|
|
20
27
|
}
|
|
@@ -22,6 +22,9 @@ class TeamPushCommand extends api_client_command_1.default {
|
|
|
22
22
|
cli_1.default.log('Team settings already up to date');
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
+
// team.json's `versions`/`aliases` fields are pull-only and intentionally left out of the
|
|
26
|
+
// update payload: team versions and aliases are only written through the explicit
|
|
27
|
+
// team:versions/team:aliases commands.
|
|
25
28
|
const update = { parentTeamSlugs: team.parentTeamSlugs };
|
|
26
29
|
if (team.zoneId !== undefined) {
|
|
27
30
|
update.zoneId = team.zoneId;
|
|
@@ -35,6 +38,9 @@ class TeamPushCommand extends api_client_command_1.default {
|
|
|
35
38
|
update.webhookAuth = team.webhookAuth
|
|
36
39
|
? { headerName: team.webhookAuth.headerName ?? null, variableKey: team.webhookAuth.variableKey }
|
|
37
40
|
: { variableKey: null };
|
|
41
|
+
// Sent unconditionally for the same reason as webhookAuth: team.json is the source of truth,
|
|
42
|
+
// so removing primaryLocale from it must clear the cloud setting. Empty string is the clear.
|
|
43
|
+
update.primaryLocale = team.primaryLocale ?? '';
|
|
38
44
|
await this._fcodeGraphQLClient.pushTeam(update);
|
|
39
45
|
await this.workspaceConfig.setTeamStoredHash(team.contentHash);
|
|
40
46
|
cli_1.default.log(color_1.default.green('✅ team settings pushed'));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ApiClientCommand from '../../../core/commands/api-client-command';
|
|
2
|
+
export default class TeamVersionsCreateCommand extends ApiClientCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
tag: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
comment: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
private logManifest;
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const color_1 = require("@oclif/color");
|
|
5
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
6
|
+
const pull_1 = require("../../pull");
|
|
7
|
+
const cli_1 = require("../../../utils/cli");
|
|
8
|
+
class TeamVersionsCreateCommand extends api_client_command_1.default {
|
|
9
|
+
async run() {
|
|
10
|
+
const { args: { tag }, flags: { comment }, } = await this.parse(TeamVersionsCreateCommand);
|
|
11
|
+
cli_1.default.log(color_1.default.cyan(`\n🏷️ publishing team version ${tag}...`));
|
|
12
|
+
const teamVersion = await this._fcodeGraphQLClient.createTeamVersion(tag, comment);
|
|
13
|
+
const failedCount = this.logManifest(teamVersion);
|
|
14
|
+
// Refresh the local workspace: per-entity versions/<tag>/ folders and team.json.
|
|
15
|
+
await pull_1.default.run([]);
|
|
16
|
+
// Release pipelines must be able to detect an incomplete version. Re-running the
|
|
17
|
+
// command converges: it publishes only what is still missing.
|
|
18
|
+
if (failedCount > 0) {
|
|
19
|
+
this.error(`team version ${tag} published with ${failedCount} failed entities`, { exit: 1 });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
logManifest(teamVersion) {
|
|
23
|
+
const created = teamVersion.manifest.filter(({ outcome }) => outcome === 'CREATED');
|
|
24
|
+
const skipped = teamVersion.manifest.filter(({ outcome }) => outcome === 'SKIPPED_EXISTS');
|
|
25
|
+
const failed = teamVersion.manifest.filter(({ outcome }) => outcome === 'FAILED');
|
|
26
|
+
cli_1.default.log(color_1.default.green(`✅ ${created.length} created`) + `, ${skipped.length} skipped (tag already exists)` + (failed.length > 0 ? `, ${color_1.default.red(`${failed.length} failed`)}` : ''));
|
|
27
|
+
for (const entry of failed) {
|
|
28
|
+
cli_1.default.error(`${entry.resourceType.toLowerCase()} ${entry.slug} — ${entry.error ?? 'unknown error'}`);
|
|
29
|
+
}
|
|
30
|
+
return failed.length;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
TeamVersionsCreateCommand.description = 'Publish a team version: creates a version with the tag on every owned process and module ' +
|
|
34
|
+
'(existing tags are skipped) and pins bare module imports in the published snapshots';
|
|
35
|
+
TeamVersionsCreateCommand.examples = ['$ fcode team:versions:create v1.0.0', '$ fcode team:versions:create v1.0.0 --comment "First stable release"'];
|
|
36
|
+
TeamVersionsCreateCommand.args = {
|
|
37
|
+
tag: core_1.Args.string({ description: 'version tag to publish (e.g. v1.0.0)', required: true }),
|
|
38
|
+
};
|
|
39
|
+
TeamVersionsCreateCommand.flags = {
|
|
40
|
+
comment: core_1.Flags.string({ char: 'c', description: 'comment describing the version' }),
|
|
41
|
+
};
|
|
42
|
+
exports.default = TeamVersionsCreateCommand;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ApiClientCommand from '../../../core/commands/api-client-command';
|
|
2
|
+
export default class TeamVersionsDeleteCommand extends ApiClientCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
tag: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const color_1 = require("@oclif/color");
|
|
5
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
6
|
+
const pull_1 = require("../../pull");
|
|
7
|
+
const types_1 = require("../../../core/types");
|
|
8
|
+
const cli_1 = require("../../../utils/cli");
|
|
9
|
+
class TeamVersionsDeleteCommand extends api_client_command_1.default {
|
|
10
|
+
async run() {
|
|
11
|
+
const { args: { tag }, flags: { force }, } = await this.parse(TeamVersionsDeleteCommand);
|
|
12
|
+
if (!force) {
|
|
13
|
+
const confirmed = await cli_1.default.confirm(`Deleting team version ${tag} also deletes every process/module version with that tag, plus the aliases, executions and schedules referencing them. Continue?`);
|
|
14
|
+
if (!confirmed) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
cli_1.default.log(color_1.default.cyan(`\n🗑️ deleting team version ${tag}...`));
|
|
19
|
+
await this._fcodeGraphQLClient.deleteTeamVersion(tag);
|
|
20
|
+
cli_1.default.log(color_1.default.green(`✅ team version ${tag} deleted`));
|
|
21
|
+
// Refresh the local workspace so removed versions/<tag>/ folders disappear.
|
|
22
|
+
await pull_1.default.run([]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
TeamVersionsDeleteCommand.description = 'Delete a team version. CASCADES: every owned process/module version with the tag is deleted, ' +
|
|
26
|
+
'together with the aliases, executions and schedules that reference them';
|
|
27
|
+
TeamVersionsDeleteCommand.examples = ['$ fcode team:versions:delete v1.0.0'];
|
|
28
|
+
TeamVersionsDeleteCommand.args = {
|
|
29
|
+
tag: core_1.Args.string({ description: 'version tag to delete', required: true }),
|
|
30
|
+
};
|
|
31
|
+
TeamVersionsDeleteCommand.flags = types_1.ForceFlag;
|
|
32
|
+
exports.default = TeamVersionsDeleteCommand;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@oclif/color");
|
|
4
|
+
const api_client_command_1 = require("../../../core/commands/api-client-command");
|
|
5
|
+
const cli_1 = require("../../../utils/cli");
|
|
6
|
+
class TeamVersionsListCommand extends api_client_command_1.default {
|
|
7
|
+
async run() {
|
|
8
|
+
await this.parse(TeamVersionsListCommand);
|
|
9
|
+
const versions = await this._fcodeGraphQLClient.allTeamVersions();
|
|
10
|
+
if (versions.length === 0) {
|
|
11
|
+
cli_1.default.log('📋 No team versions published yet.');
|
|
12
|
+
cli_1.default.log(`Publish one with ${color_1.default.cyan('fcode team:versions:create [tag]')}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
cli_1.default.log('🏷️ Team versions:\n');
|
|
16
|
+
for (const version of versions) {
|
|
17
|
+
const comment = version.comment ? ` — ${version.comment}` : '';
|
|
18
|
+
const author = version.createdBy?.name ? ` by ${version.createdBy.name}` : '';
|
|
19
|
+
cli_1.default.log(` ${color_1.default.cyan(version.tag)}${comment} (${version.createdAt}${author})`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
TeamVersionsListCommand.description = 'List the team versions published on the active team workspace';
|
|
24
|
+
TeamVersionsListCommand.examples = ['$ fcode team:versions:list'];
|
|
25
|
+
exports.default = TeamVersionsListCommand;
|
|
@@ -11,6 +11,7 @@ class VariablesStatus extends variables_command_1.default {
|
|
|
11
11
|
async resourcesTableLog(resources) {
|
|
12
12
|
const globalConfig = new global_1.default();
|
|
13
13
|
const localVariablesFileName = await globalConfig.getSetting(settings_1.SettingKeys.LOCAL_VARIABLES_FILE_NAME);
|
|
14
|
+
const hasInherited = resources.some((resource) => resource.resolvingTeamSlug);
|
|
14
15
|
cli_1.cli.table(resources, {
|
|
15
16
|
key: {
|
|
16
17
|
get({ key, status }) {
|
|
@@ -18,6 +19,16 @@ class VariablesStatus extends variables_command_1.default {
|
|
|
18
19
|
},
|
|
19
20
|
header: 'key',
|
|
20
21
|
},
|
|
22
|
+
...(hasInherited
|
|
23
|
+
? {
|
|
24
|
+
inherited: {
|
|
25
|
+
get(resource) {
|
|
26
|
+
return (0, colorize_resource_1.colorizeByStatus)(resource.resolvingTeamSlug ? `🔗 ${resource.resolvingTeamSlug}` : '', resource.status);
|
|
27
|
+
},
|
|
28
|
+
header: 'inherited',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
: {}),
|
|
21
32
|
status: {
|
|
22
33
|
get({ status }) {
|
|
23
34
|
return (0, colorize_resource_1.colorizeByStatus)(status, status);
|
package/dist/config/remote.js
CHANGED
|
@@ -47,7 +47,7 @@ class RemoteConfig {
|
|
|
47
47
|
return this;
|
|
48
48
|
}
|
|
49
49
|
init() {
|
|
50
|
-
fs.writeJsonSync(this.configPath, { slug: this.remote, modules: {}, processes: {}, variables: {}, dependencies: {}, team: {} }, { spaces: 2 });
|
|
50
|
+
fs.writeJsonSync(this.configPath, { slug: this.remote, modules: {}, processes: {}, variables: {}, i18n: {}, dependencies: {}, team: {} }, { spaces: 2 });
|
|
51
51
|
}
|
|
52
52
|
createDir(configPath) {
|
|
53
53
|
if (!fs.pathExistsSync(configPath)) {
|
|
@@ -8,6 +8,7 @@ export declare enum SettingKeys {
|
|
|
8
8
|
IGNORE_PROCESS_VERSIONS = "ignoreProcessVersions",
|
|
9
9
|
IGNORE_WORKSPACE_NOT_EMPTY = "ignoreWorkspaceNotEmpty",
|
|
10
10
|
IGNORE_FCODE_RUN_PROCESSES = "ignoreFcodeRunProcesses",
|
|
11
|
+
INHERITED_VARIABLES_FILE_NAME = "inheritedVariablesFileName",
|
|
11
12
|
LOCAL_VARIABLES_FILE_NAME = "localVariablesFileName",
|
|
12
13
|
SKIP_SKILLS_SETUP = "skipSkillsSetup",
|
|
13
14
|
VARIABLES_FILE_NAME = "variablesFileName"
|
package/dist/config/settings.js
CHANGED
|
@@ -8,6 +8,7 @@ var SettingKeys;
|
|
|
8
8
|
SettingKeys["IGNORE_PROCESS_VERSIONS"] = "ignoreProcessVersions";
|
|
9
9
|
SettingKeys["IGNORE_WORKSPACE_NOT_EMPTY"] = "ignoreWorkspaceNotEmpty";
|
|
10
10
|
SettingKeys["IGNORE_FCODE_RUN_PROCESSES"] = "ignoreFcodeRunProcesses";
|
|
11
|
+
SettingKeys["INHERITED_VARIABLES_FILE_NAME"] = "inheritedVariablesFileName";
|
|
11
12
|
SettingKeys["LOCAL_VARIABLES_FILE_NAME"] = "localVariablesFileName";
|
|
12
13
|
SettingKeys["SKIP_SKILLS_SETUP"] = "skipSkillsSetup";
|
|
13
14
|
SettingKeys["VARIABLES_FILE_NAME"] = "variablesFileName";
|
|
@@ -48,6 +49,11 @@ exports.SETTINGS_DEFINITIONS = [
|
|
|
48
49
|
default: 'variables.local.env',
|
|
49
50
|
description: 'Local variables file name',
|
|
50
51
|
},
|
|
52
|
+
{
|
|
53
|
+
key: SettingKeys.INHERITED_VARIABLES_FILE_NAME,
|
|
54
|
+
default: 'variables.inherited.env',
|
|
55
|
+
description: 'File holding the variables inherited from parent workspaces (read-only, gitignored)',
|
|
56
|
+
},
|
|
51
57
|
];
|
|
52
58
|
function getSettingDefault(key) {
|
|
53
59
|
return exports.SETTINGS_DEFINITIONS.find((def) => def.key === key)?.default;
|
package/dist/config/workspace.js
CHANGED
|
@@ -85,6 +85,12 @@ class WorkspaceConfig extends index_1.default {
|
|
|
85
85
|
else if (resourceType === 'variables') {
|
|
86
86
|
return team.variables;
|
|
87
87
|
}
|
|
88
|
+
else if (resourceType === 'i18n') {
|
|
89
|
+
// Defaulted rather than required: workspaces cloned before locales existed have no `i18n`
|
|
90
|
+
// key in their sync metadata, and pulling should populate it instead of failing.
|
|
91
|
+
team.i18n ||= {};
|
|
92
|
+
return team.i18n;
|
|
93
|
+
}
|
|
88
94
|
else if (resourceType === 'dependencies') {
|
|
89
95
|
return team.dependencies;
|
|
90
96
|
}
|
|
@@ -100,6 +106,10 @@ class WorkspaceConfig extends index_1.default {
|
|
|
100
106
|
else if (resource instanceof types_1.Variable) {
|
|
101
107
|
return team.variables;
|
|
102
108
|
}
|
|
109
|
+
else if (resource instanceof types_1.Locale) {
|
|
110
|
+
team.i18n ||= {};
|
|
111
|
+
return team.i18n;
|
|
112
|
+
}
|
|
103
113
|
else if (resource instanceof types_1.ProgrammingLanguageManifest) {
|
|
104
114
|
return team.dependencies;
|
|
105
115
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const locale_1 = require("../../repositories/locale");
|
|
4
|
+
const resource_merger_1 = require("../resource-merger");
|
|
5
|
+
const resource_pusher_1 = require("../resource-pusher");
|
|
6
|
+
const resource_command_1 = require("./resource-command");
|
|
7
|
+
class I18nCommand extends resource_command_1.default {
|
|
8
|
+
async init() {
|
|
9
|
+
await super.init();
|
|
10
|
+
this._resourceRepository = new locale_1.default();
|
|
11
|
+
this._resourceMerger = new resource_merger_1.default(this._resourceRepository);
|
|
12
|
+
this._resourcePusher = new resource_pusher_1.default(this._resourceRepository, this._fcodeGraphQLClient);
|
|
13
|
+
this._resourceQuery = () => this._fcodeGraphQLClient.allLocales.bind(this._fcodeGraphQLClient)();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.default = I18nCommand;
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export declare class LocalVariableNotFound extends ResourceNotFound {
|
|
|
30
30
|
export declare class CloudVariableNotFound extends ResourceNotFound {
|
|
31
31
|
constructor(error: Error | string, variableId: string);
|
|
32
32
|
}
|
|
33
|
+
export declare class LocalLocaleNotFound extends ResourceNotFound {
|
|
34
|
+
constructor(error: Error | string, locale: string);
|
|
35
|
+
}
|
|
36
|
+
export declare class CloudLocaleNotFound extends ResourceNotFound {
|
|
37
|
+
constructor(error: Error | string, locale: string);
|
|
38
|
+
}
|
|
33
39
|
export declare class LocalProgrammingLanguageManifestNotFound extends ResourceNotFound {
|
|
34
40
|
constructor(error: Error | string, programmingLanguageManifestId: string);
|
|
35
41
|
}
|
package/dist/core/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ErrorExecutingProcess = exports.ProcessDependenciesNotInstalled = exports.DependenciesNotInstalled = exports.NotFcodeWorkspace = exports.NotAuthenticated = exports.CloudProgrammingLanguageManifestNotFound = exports.LocalProgrammingLanguageManifestNotFound = exports.CloudVariableNotFound = exports.LocalVariableNotFound = exports.CloudScriptLibraryNotFound = exports.LocalScriptLibraryNotFound = exports.CloudProcessNotFound = exports.LocalProcessNotFound = exports.ResourceNotFound = exports.CLIError = void 0;
|
|
3
|
+
exports.ErrorExecutingProcess = exports.ProcessDependenciesNotInstalled = exports.DependenciesNotInstalled = exports.NotFcodeWorkspace = exports.NotAuthenticated = exports.CloudProgrammingLanguageManifestNotFound = exports.LocalProgrammingLanguageManifestNotFound = exports.CloudLocaleNotFound = exports.LocalLocaleNotFound = exports.CloudVariableNotFound = exports.LocalVariableNotFound = exports.CloudScriptLibraryNotFound = exports.LocalScriptLibraryNotFound = exports.CloudProcessNotFound = exports.LocalProcessNotFound = exports.ResourceNotFound = exports.CLIError = void 0;
|
|
4
4
|
const global_1 = require("../config/global");
|
|
5
5
|
class CLIError extends Error {
|
|
6
6
|
constructor(message, options) {
|
|
@@ -74,6 +74,24 @@ class CloudVariableNotFound extends ResourceNotFound {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
exports.CloudVariableNotFound = CloudVariableNotFound;
|
|
77
|
+
class LocalLocaleNotFound extends ResourceNotFound {
|
|
78
|
+
constructor(error, locale) {
|
|
79
|
+
super(error, 'Locale', locale, {
|
|
80
|
+
code: 'ERR_LOCAL_LOCALE_NOT_FOUND',
|
|
81
|
+
suggestions: [],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.LocalLocaleNotFound = LocalLocaleNotFound;
|
|
86
|
+
class CloudLocaleNotFound extends ResourceNotFound {
|
|
87
|
+
constructor(error, locale) {
|
|
88
|
+
super(error, 'Locale', locale, {
|
|
89
|
+
code: 'ERR_CLOUD_LOCALE_NOT_FOUND',
|
|
90
|
+
suggestions: [],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.CloudLocaleNotFound = CloudLocaleNotFound;
|
|
77
95
|
class LocalProgrammingLanguageManifestNotFound extends ResourceNotFound {
|
|
78
96
|
constructor(error, programmingLanguageManifestId) {
|
|
79
97
|
super(error, 'ProgrammingLanguageManifest', programmingLanguageManifestId, {
|
|
@@ -4,6 +4,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
4
4
|
const fs = require("node:fs");
|
|
5
5
|
const os = require("node:os");
|
|
6
6
|
const path = require("node:path");
|
|
7
|
+
const locale_resolver_1 = require("../../services/locale-resolver");
|
|
7
8
|
const errors_1 = require("../errors");
|
|
8
9
|
const cli_1 = require("../../utils/cli");
|
|
9
10
|
const dependencies_1 = require("../utils/dependencies");
|
|
@@ -114,6 +115,9 @@ class ExecutionTask {
|
|
|
114
115
|
},
|
|
115
116
|
variables: this.getVariablesObject(),
|
|
116
117
|
request: this._options.request ?? 'No request, you are using the CLI',
|
|
118
|
+
// Layered here rather than in the generated runtime: the cloud ships one already-merged
|
|
119
|
+
// flat map, and a local run has to resolve fcode.i18n against the same shape.
|
|
120
|
+
i18n: (0, locale_resolver_1.resolveLocalTranslations)(this._options.locale),
|
|
117
121
|
};
|
|
118
122
|
}
|
|
119
123
|
async prepare() {
|
|
@@ -111,7 +111,12 @@ const main = typeof _hooksModule['${fn}'] === 'function'
|
|
|
111
111
|
};`}
|
|
112
112
|
|
|
113
113
|
global.fcode = {
|
|
114
|
-
|
|
114
|
+
// A version is either a bare string or an options object ({ version: 'v1' }), the
|
|
115
|
+
// form every other versioned helper takes — the cloud reads both, so a local run must too.
|
|
116
|
+
import: (moduleName, tagOrOptions) => {
|
|
117
|
+
const version = typeof tagOrOptions === 'string'
|
|
118
|
+
? tagOrOptions
|
|
119
|
+
: (tagOrOptions && typeof tagOrOptions === 'object' ? tagOrOptions.version : undefined);
|
|
115
120
|
let modulePath = require('path').join('${process.cwd()}', 'modules', moduleName);
|
|
116
121
|
if(version) {
|
|
117
122
|
modulePath = require('path').join(modulePath, 'versions', version)
|
|
@@ -588,6 +593,26 @@ const main = typeof _hooksModule['${fn}'] === 'function'
|
|
|
588
593
|
}
|
|
589
594
|
};
|
|
590
595
|
})(),
|
|
596
|
+
// The third argument is the version options the cloud honours. A workspace directory holds
|
|
597
|
+
// no published snapshots, so a pinned call resolves against the files on disk — the same
|
|
598
|
+
// per-file fallback the cloud applies to a locale that was never published at that tag.
|
|
599
|
+
// Own properties only and no array arguments, exactly like the cloud executors.
|
|
600
|
+
i18n: Object.assign((key, args, _options) => {
|
|
601
|
+
const translations = ${JSON.stringify(metadata.i18n.translations, null, 2)};
|
|
602
|
+
const template = Object.hasOwn(translations, key) ? translations[key] : undefined;
|
|
603
|
+
if (template === undefined) {
|
|
604
|
+
return key;
|
|
605
|
+
}
|
|
606
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) {
|
|
607
|
+
return template;
|
|
608
|
+
}
|
|
609
|
+
// Single pass, so a substituted value containing %{...} is never rescanned.
|
|
610
|
+
return template.replace(/%\{([^}]*)\}/g, (match, name) => {
|
|
611
|
+
const trimmed = name.trim();
|
|
612
|
+
const value = Object.hasOwn(args, trimmed) ? args[trimmed] : undefined;
|
|
613
|
+
return value === undefined || value === null ? match : String(value);
|
|
614
|
+
});
|
|
615
|
+
}, { locale: ${JSON.stringify(metadata.i18n.locale ?? null)} }),
|
|
591
616
|
sendMail: async ({ to, subject, text, html } = {}) => {
|
|
592
617
|
// No manager locally: log the email contents instead of sending, so
|
|
593
618
|
// devs can see exactly what would be delivered when run in the cloud.
|
|
@@ -82,6 +82,16 @@ class ResourcePusher {
|
|
|
82
82
|
const createdVariable = await this._fcodeGraphQLClient.pushVariable(variable);
|
|
83
83
|
return this._repository.save(createdVariable);
|
|
84
84
|
}
|
|
85
|
+
if (this._repository._repositoryName === 'i18n') {
|
|
86
|
+
const locale = resource;
|
|
87
|
+
// The recorded id is only a hint: pushing an identifier a parent workspace owns has to
|
|
88
|
+
// create an override here, so the update path is taken only for a locale this workspace
|
|
89
|
+
// already owns.
|
|
90
|
+
const pushed = locale.id && !locale.resolvingTeamSlug
|
|
91
|
+
? await this._fcodeGraphQLClient.updateLocale(locale)
|
|
92
|
+
: await this._fcodeGraphQLClient.pushLocale(locale);
|
|
93
|
+
return this._repository.save(pushed);
|
|
94
|
+
}
|
|
85
95
|
if (this._repository._repositoryName === 'dependencies') {
|
|
86
96
|
const updatedProgrammingLanguageManifests = await this._fcodeGraphQLClient.pushProgrammingLanguageManifest(resource);
|
|
87
97
|
return this._repository.save(updatedProgrammingLanguageManifests);
|
|
@@ -114,6 +124,11 @@ class ResourcePusher {
|
|
|
114
124
|
if (this._repository._repositoryName === 'variables') {
|
|
115
125
|
await this._fcodeGraphQLClient.removeVariable(resource.id);
|
|
116
126
|
}
|
|
127
|
+
if (this._repository._repositoryName === 'i18n') {
|
|
128
|
+
// By identifier, not slug: an inherited locale is tracked under a qualified slug, and only
|
|
129
|
+
// this workspace's own locales — where the two coincide — ever reach a remote delete.
|
|
130
|
+
await this._fcodeGraphQLClient.removeLocale(resource.locale);
|
|
131
|
+
}
|
|
117
132
|
this._repository.removeMetadata(resource.slug);
|
|
118
133
|
}
|
|
119
134
|
catch (error) {
|