@factorialco/fcode-cli 3.5.1 → 3.6.1
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 +1 -1
- package/dist/commands/http.d.ts +2 -1
- package/dist/commands/http.js +4 -2
- package/dist/commands/processes/push.js +5 -0
- package/dist/commands/settings/pull.js +5 -0
- package/dist/commands/settings/push.d.ts +6 -0
- package/dist/commands/settings/push.js +21 -26
- package/dist/config/workspace.d.ts +6 -0
- package/dist/config/workspace.js +9 -1
- package/dist/core/errors.d.ts +14 -0
- package/dist/core/errors.js +35 -1
- package/dist/core/types.d.ts +44 -11
- package/dist/core/types.js +13 -1
- package/dist/repositories/settings.d.ts +22 -2
- package/dist/repositories/settings.js +129 -11
- package/dist/services/fcode-graphql-client.d.ts +8 -3
- package/dist/services/fcode-graphql-client.js +52 -38
- package/dist/services/locale-resolver.js +6 -2
- package/dist/services/retired-app-role.d.ts +15 -0
- package/dist/services/retired-app-role.js +44 -0
- package/dist/services/webhook-auth.d.ts +4 -2
- package/dist/services/webhook-auth.js +11 -5
- package/dist/utils/cli-version.d.ts +7 -0
- package/dist/utils/cli-version.js +20 -0
- package/dist/utils/gitignore.d.ts +2 -1
- package/dist/utils/gitignore.js +5 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g @factorialco/fcode-cli
|
|
|
20
20
|
$ fcode COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ fcode (--version)
|
|
23
|
-
@factorialco/fcode-cli/3.
|
|
23
|
+
@factorialco/fcode-cli/3.6.1 linux-x64 node-v22.23.4
|
|
24
24
|
$ fcode --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ fcode COMMAND
|
package/dist/commands/http.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export default class Http extends Command {
|
|
|
14
14
|
* Enforces the webhook's configured auth, simulating the cloud webhook endpoint: the effective
|
|
15
15
|
* configuration names a variable, whose value is looked up in the workspace variables and compared
|
|
16
16
|
* against the configured header. A missing or malformed header, an undefined variable, a mismatch,
|
|
17
|
-
* or a webhook inheriting a workspace configuration that
|
|
17
|
+
* or a webhook inheriting a workspace configuration that is cleared or neither set nor inherited, is
|
|
18
|
+
* rejected.
|
|
18
19
|
* Returns true when the request may proceed, false after writing a 403.
|
|
19
20
|
*/
|
|
20
21
|
private _checkWebhookAuth;
|
package/dist/commands/http.js
CHANGED
|
@@ -122,7 +122,8 @@ class Http extends core_1.Command {
|
|
|
122
122
|
* Enforces the webhook's configured auth, simulating the cloud webhook endpoint: the effective
|
|
123
123
|
* configuration names a variable, whose value is looked up in the workspace variables and compared
|
|
124
124
|
* against the configured header. A missing or malformed header, an undefined variable, a mismatch,
|
|
125
|
-
* or a webhook inheriting a workspace configuration that
|
|
125
|
+
* or a webhook inheriting a workspace configuration that is cleared or neither set nor inherited, is
|
|
126
|
+
* rejected.
|
|
126
127
|
* Returns true when the request may proceed, false after writing a 403.
|
|
127
128
|
*/
|
|
128
129
|
async _checkWebhookAuth(req, res, processRepository, settingsRepository, slug) {
|
|
@@ -137,9 +138,10 @@ class Http extends core_1.Command {
|
|
|
137
138
|
// Read per request, like the process metadata above, so editing settings.json while the server
|
|
138
139
|
// runs takes effect without a restart.
|
|
139
140
|
const teamWebhookAuth = (await settingsRepository.find()).webhookAuth;
|
|
141
|
+
const inheritedTeamWebhookAuth = settingsRepository.findInherited().webhookAuth;
|
|
140
142
|
let effectiveAuth;
|
|
141
143
|
try {
|
|
142
|
-
effectiveAuth = (0, webhook_auth_1.resolveEffectiveAuth)(webhook, teamWebhookAuth);
|
|
144
|
+
effectiveAuth = (0, webhook_auth_1.resolveEffectiveAuth)(webhook, teamWebhookAuth, inheritedTeamWebhookAuth);
|
|
143
145
|
}
|
|
144
146
|
catch (error) {
|
|
145
147
|
// Fails closed, and says why: otherwise this reads as a wrong token rather than a local
|
|
@@ -5,6 +5,7 @@ const processes_command_1 = require("../../core/commands/processes-command");
|
|
|
5
5
|
const types_1 = require("../../core/types");
|
|
6
6
|
const cli_1 = require("../../utils/cli");
|
|
7
7
|
const settings_1 = require("../../config/settings");
|
|
8
|
+
const retired_app_role_1 = require("../../services/retired-app-role");
|
|
8
9
|
class ProcessesPush extends processes_command_1.default {
|
|
9
10
|
async pushVersioned() {
|
|
10
11
|
const processes = (await this._resourceRepository.findAll()).filter((process) => process.id && !process.deletion);
|
|
@@ -25,6 +26,10 @@ class ProcessesPush extends processes_command_1.default {
|
|
|
25
26
|
await Promise.allSettled(allPromises);
|
|
26
27
|
}
|
|
27
28
|
async run() {
|
|
29
|
+
const appRoleError = (0, retired_app_role_1.retiredAppRoleError)(await this.getLocalResources());
|
|
30
|
+
if (appRoleError) {
|
|
31
|
+
this.error(appRoleError);
|
|
32
|
+
}
|
|
28
33
|
await super.pushResources();
|
|
29
34
|
const ignoreProcessVersions = (await this.globalConfig.getSetting(settings_1.SettingKeys.IGNORE_PROCESS_VERSIONS)) === 'true';
|
|
30
35
|
if (!ignoreProcessVersions) {
|
|
@@ -5,6 +5,7 @@ const api_client_command_1 = require("../../core/commands/api-client-command");
|
|
|
5
5
|
const settings_1 = require("../../repositories/settings");
|
|
6
6
|
const types_1 = require("../../core/types");
|
|
7
7
|
const cli_1 = require("../../utils/cli");
|
|
8
|
+
const gitignore_1 = require("../../utils/gitignore");
|
|
8
9
|
class SettingsPullCommand extends api_client_command_1.default {
|
|
9
10
|
constructor() {
|
|
10
11
|
super(...arguments);
|
|
@@ -32,6 +33,10 @@ class SettingsPullCommand extends api_client_command_1.default {
|
|
|
32
33
|
const settings = await this._settingsRepository.fromRaw(raw);
|
|
33
34
|
this._settingsRepository.applyCloudVersioning(settings, versions, aliases);
|
|
34
35
|
this._settingsRepository.save(settings);
|
|
36
|
+
this._settingsRepository.saveInherited(await this._settingsRepository.inheritedFromRaw(raw));
|
|
37
|
+
// A read-only copy of the parents' values: keep it out of git even when this command runs on
|
|
38
|
+
// its own rather than inside `fcode pull`.
|
|
39
|
+
await (0, gitignore_1.syncInheritedGitignore)();
|
|
35
40
|
await this.workspaceConfig.setSettingsStoredHash(settings.contentHash);
|
|
36
41
|
}
|
|
37
42
|
}
|
|
@@ -8,6 +8,12 @@ export default class SettingsPushCommand extends ApiClientCommand {
|
|
|
8
8
|
};
|
|
9
9
|
_settingsRepository: SettingsRepository;
|
|
10
10
|
run(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* A settings.json some CLI synced (there is a stored hash) without marking it inheritance-aware was
|
|
13
|
+
* written by a CLI older than settings inheritance, with every effective value in it. Only a
|
|
14
|
+
* workspace with parents is affected: without any, its effective settings are its own.
|
|
15
|
+
*/
|
|
16
|
+
private pulledByAPreInheritanceCli;
|
|
11
17
|
/** Makes the cloud's versions/aliases match settings.json; returns failed manifest entries. */
|
|
12
18
|
private syncVersioning;
|
|
13
19
|
}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const color_1 = require("@oclif/color");
|
|
4
4
|
const api_client_command_1 = require("../../core/commands/api-client-command");
|
|
5
|
+
const errors_1 = require("../../core/errors");
|
|
5
6
|
const settings_1 = require("../../repositories/settings");
|
|
6
7
|
const types_1 = require("../../core/types");
|
|
7
8
|
const team_versioning_1 = require("../../services/team-versioning");
|
|
8
9
|
const cli_1 = require("../../utils/cli");
|
|
10
|
+
const gitignore_1 = require("../../utils/gitignore");
|
|
9
11
|
class SettingsPushCommand extends api_client_command_1.default {
|
|
10
12
|
constructor() {
|
|
11
13
|
super(...arguments);
|
|
@@ -23,30 +25,19 @@ class SettingsPushCommand extends api_client_command_1.default {
|
|
|
23
25
|
cli_1.default.log('Workspace settings already up to date');
|
|
24
26
|
return;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
update.zoneId = settings.zoneId;
|
|
28
|
+
if (await this.pulledByAPreInheritanceCli(settings, storedHash)) {
|
|
29
|
+
throw new errors_1.PreInheritanceSettingsFile();
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
update.webhookAuth = settings.webhookAuth
|
|
37
|
-
? { headerName: settings.webhookAuth.headerName ?? null, variableKey: settings.webhookAuth.variableKey }
|
|
38
|
-
: { variableKey: null };
|
|
39
|
-
// Sent unconditionally for the same reason as webhookAuth: settings.json is the source of truth,
|
|
40
|
-
// so removing primaryLocale from it must clear the cloud setting. Empty string is the clear.
|
|
41
|
-
update.primaryLocale = settings.primaryLocale ?? '';
|
|
42
|
-
await this._fcodeGraphQLClient.pushTeam(update);
|
|
31
|
+
const pushed = await this._fcodeGraphQLClient.pushTeam(await this._settingsRepository.toTeamUpdate(settings));
|
|
32
|
+
// What the workspace now inherits follows from what was just pushed.
|
|
33
|
+
this._settingsRepository.saveInherited(await this._settingsRepository.inheritedFromRaw(pushed));
|
|
34
|
+
// A read-only copy of the parents' values: keep it out of git even when this command runs on
|
|
35
|
+
// its own rather than inside `fcode pull`.
|
|
36
|
+
await (0, gitignore_1.syncInheritedGitignore)();
|
|
43
37
|
const failedEntities = await this.syncVersioning(settings, force);
|
|
44
38
|
// Re-read the cloud state so server-assigned fields (createdAt) and declined deletions
|
|
45
39
|
// land back in settings.json, then hash exactly what was written.
|
|
46
|
-
const [versions, aliases] = await Promise.all([
|
|
47
|
-
this._fcodeGraphQLClient.allTeamVersions(),
|
|
48
|
-
this._fcodeGraphQLClient.allTeamVersionAliases(),
|
|
49
|
-
]);
|
|
40
|
+
const [versions, aliases] = await Promise.all([this._fcodeGraphQLClient.allTeamVersions(), this._fcodeGraphQLClient.allTeamVersionAliases()]);
|
|
50
41
|
this._settingsRepository.applyCloudVersioning(settings, versions, aliases);
|
|
51
42
|
this._settingsRepository.save(settings);
|
|
52
43
|
await this.workspaceConfig.setSettingsStoredHash(settings.contentHash);
|
|
@@ -57,12 +48,17 @@ class SettingsPushCommand extends api_client_command_1.default {
|
|
|
57
48
|
this.error(`workspace versions pushed with ${failedEntities} failed entities`, { exit: 1 });
|
|
58
49
|
}
|
|
59
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* A settings.json some CLI synced (there is a stored hash) without marking it inheritance-aware was
|
|
53
|
+
* written by a CLI older than settings inheritance, with every effective value in it. Only a
|
|
54
|
+
* workspace with parents is affected: without any, its effective settings are its own.
|
|
55
|
+
*/
|
|
56
|
+
async pulledByAPreInheritanceCli(settings, storedHash) {
|
|
57
|
+
return storedHash !== undefined && settings.parentTeams.length > 0 && !(await this.workspaceConfig.isSettingsInheritanceAware());
|
|
58
|
+
}
|
|
60
59
|
/** Makes the cloud's versions/aliases match settings.json; returns failed manifest entries. */
|
|
61
60
|
async syncVersioning(settings, force) {
|
|
62
|
-
const [cloudVersions, cloudAliases] = await Promise.all([
|
|
63
|
-
this._fcodeGraphQLClient.allTeamVersions(),
|
|
64
|
-
this._fcodeGraphQLClient.allTeamVersionAliases(),
|
|
65
|
-
]);
|
|
61
|
+
const [cloudVersions, cloudAliases] = await Promise.all([this._fcodeGraphQLClient.allTeamVersions(), this._fcodeGraphQLClient.allTeamVersionAliases()]);
|
|
66
62
|
const plan = (0, team_versioning_1.planTeamVersioningSync)(settings, cloudVersions, cloudAliases);
|
|
67
63
|
if ((0, team_versioning_1.teamVersioningSyncPlanIsEmpty)(plan)) {
|
|
68
64
|
return 0;
|
|
@@ -96,8 +92,7 @@ class SettingsPushCommand extends api_client_command_1.default {
|
|
|
96
92
|
return failedEntities;
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
|
-
SettingsPushCommand.description = 'Update cloud workspace settings (timezone, error handler, inheritance, webhook auth) and sync ' +
|
|
100
|
-
'workspace versions/aliases from settings.json';
|
|
95
|
+
SettingsPushCommand.description = 'Update cloud workspace settings (timezone, error handler, inheritance, webhook auth) and sync ' + 'workspace versions/aliases from settings.json';
|
|
101
96
|
SettingsPushCommand.examples = ['$ fcode settings:push'];
|
|
102
97
|
SettingsPushCommand.flags = types_1.ForceFlag;
|
|
103
98
|
exports.default = SettingsPushCommand;
|
|
@@ -21,6 +21,12 @@ declare class WorkspaceConfig extends Config {
|
|
|
21
21
|
getStoredHash(resource: Resource): Promise<string>;
|
|
22
22
|
getSettingsStoredHash(): Promise<string | undefined>;
|
|
23
23
|
setSettingsStoredHash(contentHash: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Whether settings.json was last pulled or pushed by a CLI that knows settings inherit. One that
|
|
26
|
+
* did not wrote the values the workspace inherits as if they were its own, and records the hash
|
|
27
|
+
* without this mark.
|
|
28
|
+
*/
|
|
29
|
+
isSettingsInheritanceAware(): Promise<boolean>;
|
|
24
30
|
add(resourceType: string, resourceMetadata: ResourceMetadata): Promise<void>;
|
|
25
31
|
remove(resourceType: string, slug: string): Promise<void>;
|
|
26
32
|
}
|
package/dist/config/workspace.js
CHANGED
|
@@ -139,7 +139,15 @@ class WorkspaceConfig extends index_1.default {
|
|
|
139
139
|
}
|
|
140
140
|
async setSettingsStoredHash(contentHash) {
|
|
141
141
|
const team = await this.getActiveTeam();
|
|
142
|
-
await this.setTeam({ ...team, team: { contentHash } });
|
|
142
|
+
await this.setTeam({ ...team, team: { contentHash, inheritanceAware: true } });
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Whether settings.json was last pulled or pushed by a CLI that knows settings inherit. One that
|
|
146
|
+
* did not wrote the values the workspace inherits as if they were its own, and records the hash
|
|
147
|
+
* without this mark.
|
|
148
|
+
*/
|
|
149
|
+
async isSettingsInheritanceAware() {
|
|
150
|
+
return (await this.getActiveTeam()).team?.inheritanceAware === true;
|
|
143
151
|
}
|
|
144
152
|
async add(resourceType, resourceMetadata) {
|
|
145
153
|
const team = await this.getActiveTeam();
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -54,6 +54,13 @@ export declare class TeamAccessDenied extends CLIError {
|
|
|
54
54
|
export declare class NotFcodeWorkspace extends CLIError {
|
|
55
55
|
constructor();
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* settings.json was last synced by a CLI older than settings inheritance, which wrote the settings
|
|
59
|
+
* the workspace inherits as if they were its own: pushing it would stop the workspace inheriting them.
|
|
60
|
+
*/
|
|
61
|
+
export declare class PreInheritanceSettingsFile extends CLIError {
|
|
62
|
+
constructor();
|
|
63
|
+
}
|
|
57
64
|
export declare class DependenciesNotInstalled extends CLIError {
|
|
58
65
|
constructor(programmingLanguage: ProgrammingLanguage);
|
|
59
66
|
}
|
|
@@ -63,3 +70,10 @@ export declare class ProcessDependenciesNotInstalled extends CLIError {
|
|
|
63
70
|
export declare class ErrorExecutingProcess extends CLIError {
|
|
64
71
|
constructor(message: string);
|
|
65
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* The platform answered without the team's inheritable settings: it predates settings inheritance,
|
|
75
|
+
* and reading its settings would take every one of them for inherited.
|
|
76
|
+
*/
|
|
77
|
+
export declare class PlatformPredatesSettingsInheritance extends CLIError {
|
|
78
|
+
constructor();
|
|
79
|
+
}
|
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.TeamAccessDenied = 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;
|
|
3
|
+
exports.PlatformPredatesSettingsInheritance = exports.ErrorExecutingProcess = exports.ProcessDependenciesNotInstalled = exports.DependenciesNotInstalled = exports.PreInheritanceSettingsFile = exports.NotFcodeWorkspace = exports.TeamAccessDenied = 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
|
exports.teamAccessDeniedMessage = teamAccessDeniedMessage;
|
|
5
5
|
const global_1 = require("../config/global");
|
|
6
6
|
class CLIError extends Error {
|
|
@@ -145,6 +145,25 @@ class NotFcodeWorkspace extends CLIError {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
exports.NotFcodeWorkspace = NotFcodeWorkspace;
|
|
148
|
+
/**
|
|
149
|
+
* settings.json was last synced by a CLI older than settings inheritance, which wrote the settings
|
|
150
|
+
* the workspace inherits as if they were its own: pushing it would stop the workspace inheriting them.
|
|
151
|
+
*/
|
|
152
|
+
class PreInheritanceSettingsFile extends CLIError {
|
|
153
|
+
constructor() {
|
|
154
|
+
super('settings.json was last synced by an fcode CLI that predates settings inheritance: it holds the settings this ' +
|
|
155
|
+
'workspace inherits from its parent workspaces as if they were its own, and pushing it would stop the ' +
|
|
156
|
+
'workspace inheriting them.', {
|
|
157
|
+
code: 'ERR_PRE_INHERITANCE_SETTINGS_FILE',
|
|
158
|
+
exit: 1,
|
|
159
|
+
suggestions: [
|
|
160
|
+
'fcode settings:pull, then push again',
|
|
161
|
+
'fcode settings:pull --force if settings.json has local changes, then reapply them and push again',
|
|
162
|
+
],
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.PreInheritanceSettingsFile = PreInheritanceSettingsFile;
|
|
148
167
|
class DependenciesNotInstalled extends CLIError {
|
|
149
168
|
constructor(programmingLanguage) {
|
|
150
169
|
super(`Dependencies not installed.`, {
|
|
@@ -175,3 +194,18 @@ class ErrorExecutingProcess extends CLIError {
|
|
|
175
194
|
}
|
|
176
195
|
}
|
|
177
196
|
exports.ErrorExecutingProcess = ErrorExecutingProcess;
|
|
197
|
+
/**
|
|
198
|
+
* The platform answered without the team's inheritable settings: it predates settings inheritance,
|
|
199
|
+
* and reading its settings would take every one of them for inherited.
|
|
200
|
+
*/
|
|
201
|
+
class PlatformPredatesSettingsInheritance extends CLIError {
|
|
202
|
+
constructor() {
|
|
203
|
+
super('This Factorial Code platform predates settings inheritance, which this fcode CLI requires to read and ' +
|
|
204
|
+
'push workspace settings.', {
|
|
205
|
+
code: 'ERR_PLATFORM_PREDATES_SETTINGS_INHERITANCE',
|
|
206
|
+
exit: 1,
|
|
207
|
+
suggestions: ['Wait for the platform to be updated, or use an fcode CLI older than 3.6.0 until then'],
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.PlatformPredatesSettingsInheritance = PlatformPredatesSettingsInheritance;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ export interface TeamMetadata {
|
|
|
49
49
|
};
|
|
50
50
|
team?: {
|
|
51
51
|
contentHash?: string;
|
|
52
|
+
inheritanceAware?: boolean;
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
export interface RemoteMetadata {
|
|
@@ -141,6 +142,10 @@ export type AppRole = 'INSTALL' | 'SETTINGS' | 'USER_FACING_FORM' | 'UNINSTALL'
|
|
|
141
142
|
export type FormAuthMode = 'NONE' | 'FACTORIAL';
|
|
142
143
|
export interface ProcessForm {
|
|
143
144
|
enabled: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* @deprecated Retired: push refuses it and pull no longer writes it. Kept so a metadata.json that
|
|
147
|
+
* still carries it can be read and reported.
|
|
148
|
+
*/
|
|
144
149
|
appRole?: AppRole;
|
|
145
150
|
authMode?: FormAuthMode;
|
|
146
151
|
}
|
|
@@ -397,24 +402,34 @@ export interface ParentTeamRef {
|
|
|
397
402
|
slug: string;
|
|
398
403
|
version?: string;
|
|
399
404
|
}
|
|
400
|
-
/**
|
|
405
|
+
/**
|
|
406
|
+
* The workspace's settings.json: what `fcode settings:pull|push` syncs against the cloud team. It
|
|
407
|
+
* holds the team's own values only: an absent setting inherits, and null (error handler and webhook
|
|
408
|
+
* auth only) clears it, a parent's value included.
|
|
409
|
+
*/
|
|
401
410
|
export declare class WorkspaceSettings extends Resource {
|
|
402
411
|
id: string | undefined;
|
|
403
412
|
slug: string;
|
|
404
413
|
zoneId?: string;
|
|
405
|
-
errorHandlerConfig?: ErrorHandlerConfig;
|
|
414
|
+
errorHandlerConfig?: ErrorHandlerConfig | null;
|
|
406
415
|
parentTeams: ParentTeamRef[];
|
|
407
|
-
webhookAuth?: WebhookAuth;
|
|
416
|
+
webhookAuth?: WebhookAuth | null;
|
|
408
417
|
primaryLocale?: string;
|
|
409
418
|
versions?: TeamVersionFileEntry[];
|
|
410
419
|
aliases?: TeamAliasFileEntry[];
|
|
411
|
-
constructor(slug: string, parentTeams: ParentTeamRef[], zoneId?: string, errorHandlerConfig?: ErrorHandlerConfig, webhookAuth?: WebhookAuth, primaryLocale?: string);
|
|
420
|
+
constructor(slug: string, parentTeams: ParentTeamRef[], zoneId?: string, errorHandlerConfig?: ErrorHandlerConfig | null, webhookAuth?: WebhookAuth | null, primaryLocale?: string);
|
|
412
421
|
get contentHash(): string;
|
|
413
422
|
}
|
|
414
423
|
export interface RawErrorHandlerConfig {
|
|
415
424
|
processId: string;
|
|
416
425
|
tag?: string | null;
|
|
417
426
|
}
|
|
427
|
+
export type InheritableSetting = 'ERROR_HANDLER' | 'PARAMS_SCHEMA_VALIDATION' | 'PRIMARY_LOCALE' | 'WEBHOOK_AUTH' | 'ZONE_ID';
|
|
428
|
+
export type SettingMode = 'INHERIT' | 'NONE' | 'OWN';
|
|
429
|
+
export interface SettingOrigin {
|
|
430
|
+
mode: SettingMode;
|
|
431
|
+
from?: string | null;
|
|
432
|
+
}
|
|
418
433
|
export interface RawTeamSettings {
|
|
419
434
|
slug: string;
|
|
420
435
|
zoneId?: string;
|
|
@@ -422,16 +437,34 @@ export interface RawTeamSettings {
|
|
|
422
437
|
parentTeams: ParentTeamRef[];
|
|
423
438
|
webhookAuth?: WebhookAuth | null;
|
|
424
439
|
primaryLocale?: string | null;
|
|
440
|
+
origins?: Record<InheritableSetting, SettingOrigin>;
|
|
425
441
|
}
|
|
426
|
-
export interface
|
|
442
|
+
export interface InheritedWorkspaceSettings {
|
|
427
443
|
zoneId?: string;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
webhookAuth?: {
|
|
431
|
-
headerName?: string | null;
|
|
432
|
-
variableKey?: string | null;
|
|
433
|
-
} | null;
|
|
444
|
+
errorHandlerConfig?: ErrorHandlerConfig;
|
|
445
|
+
webhookAuth?: WebhookAuth;
|
|
434
446
|
primaryLocale?: string;
|
|
447
|
+
inheritedFrom: Record<string, string>;
|
|
448
|
+
}
|
|
449
|
+
export type SettingInput<T> = {
|
|
450
|
+
mode: 'INHERIT';
|
|
451
|
+
} | {
|
|
452
|
+
mode: 'NONE';
|
|
453
|
+
} | {
|
|
454
|
+
mode: 'OWN';
|
|
455
|
+
value: T;
|
|
456
|
+
};
|
|
457
|
+
export interface TeamUpdateInput {
|
|
458
|
+
parentTeams?: ParentTeamRef[];
|
|
459
|
+
inheritableSettings?: {
|
|
460
|
+
zoneId?: SettingInput<string>;
|
|
461
|
+
errorHandlerConfig?: SettingInput<RawErrorHandlerConfig>;
|
|
462
|
+
webhookAuth?: SettingInput<{
|
|
463
|
+
headerName?: string | null;
|
|
464
|
+
variableKey: string;
|
|
465
|
+
}>;
|
|
466
|
+
primaryLocale?: SettingInput<string>;
|
|
467
|
+
};
|
|
435
468
|
}
|
|
436
469
|
export type ResourceMetadata = {
|
|
437
470
|
[key: string]: any;
|
package/dist/core/types.js
CHANGED
|
@@ -261,7 +261,11 @@ class ProgrammingLanguageManifest extends Resource {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
exports.ProgrammingLanguageManifest = ProgrammingLanguageManifest;
|
|
264
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* The workspace's settings.json: what `fcode settings:pull|push` syncs against the cloud team. It
|
|
266
|
+
* holds the team's own values only: an absent setting inherits, and null (error handler and webhook
|
|
267
|
+
* auth only) clears it, a parent's value included.
|
|
268
|
+
*/
|
|
265
269
|
class WorkspaceSettings extends Resource {
|
|
266
270
|
constructor(slug, parentTeams, zoneId, errorHandlerConfig, webhookAuth, primaryLocale) {
|
|
267
271
|
super();
|
|
@@ -293,6 +297,14 @@ class WorkspaceSettings extends Resource {
|
|
|
293
297
|
if (this.primaryLocale) {
|
|
294
298
|
payload.primaryLocale = this.primaryLocale;
|
|
295
299
|
}
|
|
300
|
+
// Cleared settings hash apart from inherited ones; inserted only when any is cleared.
|
|
301
|
+
const cleared = [
|
|
302
|
+
...(this.errorHandlerConfig === null ? ['ERROR_HANDLER'] : []),
|
|
303
|
+
...(this.webhookAuth === null ? ['WEBHOOK_AUTH'] : []),
|
|
304
|
+
];
|
|
305
|
+
if (cleared.length > 0) {
|
|
306
|
+
payload.cleared = cleared;
|
|
307
|
+
}
|
|
296
308
|
// Versions hash only their tags and aliases their name->tag pairs: createdAt is
|
|
297
309
|
// server-assigned and a comment is immutable once published (there is no update mutation),
|
|
298
310
|
// so neither can represent a pushable local change. Inserted only when non-empty, again to
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { RawTeamSettings, TeamVersion, TeamVersionAlias, WorkspaceSettings } from '../core/types';
|
|
1
|
+
import { InheritedWorkspaceSettings, RawTeamSettings, TeamUpdateInput, TeamVersion, TeamVersionAlias, WorkspaceSettings } from '../core/types';
|
|
2
2
|
import WorkspaceConfig from '../config/workspace';
|
|
3
3
|
export declare const SETTINGS_FILENAME = "settings.json";
|
|
4
|
+
export declare const INHERITED_SETTINGS_FILENAME = "settings.inherited.json";
|
|
4
5
|
/**
|
|
5
6
|
* Workspaces cloned before the file was renamed carry team.json. Renaming it on first access keeps
|
|
6
7
|
* the stored sync hash valid (it is computed over the content, not the name), so the workspace does
|
|
@@ -22,8 +23,23 @@ export default class SettingsRepository {
|
|
|
22
23
|
exists(): boolean;
|
|
23
24
|
find(): Promise<WorkspaceSettings>;
|
|
24
25
|
save(settings: WorkspaceSettings): void;
|
|
25
|
-
/**
|
|
26
|
+
/** The settings a local run inherits; empty when the workspace inherits none. */
|
|
27
|
+
findInherited(): InheritedWorkspaceSettings;
|
|
28
|
+
/** Writes settings.inherited.json, or removes it when the workspace inherits no setting. */
|
|
29
|
+
saveInherited(inherited: InheritedWorkspaceSettings | undefined): void;
|
|
30
|
+
/**
|
|
31
|
+
* Builds the settings.json view of the cloud's raw payload: only the team's own values, null for a
|
|
32
|
+
* cleared setting, and nothing for an inherited or default one. Resolves the error-handler id to a
|
|
33
|
+
* slug.
|
|
34
|
+
*/
|
|
26
35
|
fromRaw(raw: RawTeamSettings): Promise<WorkspaceSettings>;
|
|
36
|
+
/** The settings.inherited.json view of the cloud's raw payload; undefined when nothing is inherited. */
|
|
37
|
+
inheritedFromRaw(raw: RawTeamSettings): Promise<InheritedWorkspaceSettings | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* settings.json is the source of truth for the team's own values: a setting absent from it is
|
|
40
|
+
* reset to inherit, null leaves the team with none (a parent's included), and a value sets it.
|
|
41
|
+
*/
|
|
42
|
+
toTeamUpdate(settings: WorkspaceSettings): Promise<TeamUpdateInput>;
|
|
27
43
|
/**
|
|
28
44
|
* Writes the cloud's workspace versions and aliases into the settings as the settings.json
|
|
29
45
|
* versioning fields (sorted for stable git diffs). Cloud entries always carry a createdAt; only
|
|
@@ -32,6 +48,10 @@ export default class SettingsRepository {
|
|
|
32
48
|
applyCloudVersioning(settings: WorkspaceSettings, versions: TeamVersion[], aliases: TeamVersionAlias[]): void;
|
|
33
49
|
resolveSlugToId(processSlug: string): Promise<string>;
|
|
34
50
|
resolveIdToSlug(processId: string): Promise<string>;
|
|
51
|
+
/** Where each setting comes from; an API without it predates settings inheritance. */
|
|
52
|
+
private originsOf;
|
|
53
|
+
private resolveErrorHandler;
|
|
54
|
+
private parseString;
|
|
35
55
|
private parseErrorHandler;
|
|
36
56
|
/**
|
|
37
57
|
* settings.json is hand-edited to declare versions, so entries without a tag are dropped and a
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SETTINGS_FILENAME = void 0;
|
|
3
|
+
exports.INHERITED_SETTINGS_FILENAME = exports.SETTINGS_FILENAME = void 0;
|
|
4
4
|
exports.migrateLegacySettingsFile = migrateLegacySettingsFile;
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const path = require("node:path");
|
|
7
7
|
const types_1 = require("../core/types");
|
|
8
8
|
const workspace_1 = require("../config/workspace");
|
|
9
|
+
const errors_1 = require("../core/errors");
|
|
9
10
|
const file_utils_1 = require("../utils/file-utils");
|
|
10
11
|
exports.SETTINGS_FILENAME = 'settings.json';
|
|
12
|
+
exports.INHERITED_SETTINGS_FILENAME = 'settings.inherited.json';
|
|
13
|
+
/** The inheritable settings settings.json manages, by their field name there. */
|
|
14
|
+
const SETTING_FIELDS = {
|
|
15
|
+
ZONE_ID: 'zoneId',
|
|
16
|
+
ERROR_HANDLER: 'errorHandlerConfig',
|
|
17
|
+
WEBHOOK_AUTH: 'webhookAuth',
|
|
18
|
+
PRIMARY_LOCALE: 'primaryLocale',
|
|
19
|
+
};
|
|
11
20
|
const LEGACY_SETTINGS_FILENAME = 'team.json';
|
|
12
21
|
/**
|
|
13
22
|
* Workspaces cloned before the file was renamed carry team.json. Renaming it on first access keeps
|
|
@@ -22,6 +31,14 @@ function migrateLegacySettingsFile(workspaceDir = process.cwd()) {
|
|
|
22
31
|
fs.renameSync(legacy, current);
|
|
23
32
|
}
|
|
24
33
|
}
|
|
34
|
+
/** A setting settings.json holds a value of or leaves out: its own value, or inherit. */
|
|
35
|
+
function ownOrInherit(value) {
|
|
36
|
+
return value === undefined ? { mode: 'INHERIT' } : { mode: 'OWN', value };
|
|
37
|
+
}
|
|
38
|
+
/** A setting settings.json leaves out (inherit) or sets to null (none). */
|
|
39
|
+
function noneOrInherit(value) {
|
|
40
|
+
return value === null ? { mode: 'NONE' } : { mode: 'INHERIT' };
|
|
41
|
+
}
|
|
25
42
|
/**
|
|
26
43
|
* Reads/writes the singleton settings.json (timezone, error handler, inheritance, webhook auth) at
|
|
27
44
|
* the workspace root. Unlike collection resources this is not a ResourceRepositoryBase:
|
|
@@ -47,7 +64,7 @@ class SettingsRepository {
|
|
|
47
64
|
raw = fs.readJsonSync(exports.SETTINGS_FILENAME) || {};
|
|
48
65
|
}
|
|
49
66
|
catch { }
|
|
50
|
-
const settings = new types_1.WorkspaceSettings(slug, this.parseParentTeams(raw.parentTeams), raw.zoneId,
|
|
67
|
+
const settings = new types_1.WorkspaceSettings(slug, this.parseParentTeams(raw.parentTeams), this.parseString(raw.zoneId), raw.errorHandlerConfig === null ? null : this.parseErrorHandler(raw.errorHandlerConfig), raw.webhookAuth === null ? null : this.parseWebhookAuth(raw.webhookAuth), this.parseString(raw.primaryLocale));
|
|
51
68
|
if (Array.isArray(raw.versions)) {
|
|
52
69
|
settings.versions = this.parseVersions(raw.versions);
|
|
53
70
|
}
|
|
@@ -60,16 +77,97 @@ class SettingsRepository {
|
|
|
60
77
|
migrateLegacySettingsFile();
|
|
61
78
|
(0, file_utils_1.outputFileSyncIfChanged)(exports.SETTINGS_FILENAME, JSON.stringify(this.toFile(settings), null, 2));
|
|
62
79
|
}
|
|
63
|
-
/**
|
|
80
|
+
/** The settings a local run inherits; empty when the workspace inherits none. */
|
|
81
|
+
findInherited() {
|
|
82
|
+
let raw = {};
|
|
83
|
+
try {
|
|
84
|
+
raw = fs.readJsonSync(exports.INHERITED_SETTINGS_FILENAME) || {};
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
return {
|
|
88
|
+
zoneId: this.parseString(raw.zoneId),
|
|
89
|
+
errorHandlerConfig: this.parseErrorHandler(raw.errorHandlerConfig),
|
|
90
|
+
webhookAuth: this.parseWebhookAuth(raw.webhookAuth),
|
|
91
|
+
primaryLocale: this.parseString(raw.primaryLocale),
|
|
92
|
+
inheritedFrom: raw.inheritedFrom ?? {},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Writes settings.inherited.json, or removes it when the workspace inherits no setting. */
|
|
96
|
+
saveInherited(inherited) {
|
|
97
|
+
if (!inherited) {
|
|
98
|
+
if (fs.pathExistsSync(exports.INHERITED_SETTINGS_FILENAME)) {
|
|
99
|
+
// Written read-only, so it has to be made writable before it can go.
|
|
100
|
+
fs.chmodSync(exports.INHERITED_SETTINGS_FILENAME, 0o644);
|
|
101
|
+
fs.removeSync(exports.INHERITED_SETTINGS_FILENAME);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// Read-only like the other inherited files: the values belong to a parent workspace.
|
|
106
|
+
(0, file_utils_1.outputFileSyncIfChanged)(exports.INHERITED_SETTINGS_FILENAME, JSON.stringify(inherited, null, 2), { readOnly: true });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Builds the settings.json view of the cloud's raw payload: only the team's own values, null for a
|
|
110
|
+
* cleared setting, and nothing for an inherited or default one. Resolves the error-handler id to a
|
|
111
|
+
* slug.
|
|
112
|
+
*/
|
|
64
113
|
async fromRaw(raw) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
114
|
+
const origins = this.originsOf(raw);
|
|
115
|
+
const own = (setting, value) => {
|
|
116
|
+
const { mode } = origins[setting];
|
|
117
|
+
if (mode === 'NONE') {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return mode === 'OWN' ? value() : undefined;
|
|
121
|
+
};
|
|
122
|
+
return new types_1.WorkspaceSettings(raw.slug, raw.parentTeams ?? [], own('ZONE_ID', () => this.parseString(raw.zoneId)) ?? undefined, await own('ERROR_HANDLER', () => this.resolveErrorHandler(raw)), own('WEBHOOK_AUTH', () => this.parseWebhookAuth(raw.webhookAuth)), own('PRIMARY_LOCALE', () => this.parseString(raw.primaryLocale)) ?? undefined);
|
|
123
|
+
}
|
|
124
|
+
/** The settings.inherited.json view of the cloud's raw payload; undefined when nothing is inherited. */
|
|
125
|
+
async inheritedFromRaw(raw) {
|
|
126
|
+
const origins = this.originsOf(raw);
|
|
127
|
+
const inheritedFrom = {};
|
|
128
|
+
for (const [setting, field] of Object.entries(SETTING_FIELDS)) {
|
|
129
|
+
const { mode, from } = origins[setting];
|
|
130
|
+
if (mode === 'INHERIT' && from) {
|
|
131
|
+
inheritedFrom[field] = from;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (Object.keys(inheritedFrom).length === 0) {
|
|
135
|
+
return undefined;
|
|
71
136
|
}
|
|
72
|
-
|
|
137
|
+
// JSON.stringify drops the settings left undefined, so the file lists only inherited ones.
|
|
138
|
+
return {
|
|
139
|
+
zoneId: inheritedFrom.zoneId ? this.parseString(raw.zoneId) : undefined,
|
|
140
|
+
errorHandlerConfig: inheritedFrom.errorHandlerConfig ? await this.resolveErrorHandler(raw) : undefined,
|
|
141
|
+
webhookAuth: inheritedFrom.webhookAuth ? this.parseWebhookAuth(raw.webhookAuth) : undefined,
|
|
142
|
+
primaryLocale: inheritedFrom.primaryLocale ? this.parseString(raw.primaryLocale) : undefined,
|
|
143
|
+
inheritedFrom,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* settings.json is the source of truth for the team's own values: a setting absent from it is
|
|
148
|
+
* reset to inherit, null leaves the team with none (a parent's included), and a value sets it.
|
|
149
|
+
*/
|
|
150
|
+
async toTeamUpdate(settings) {
|
|
151
|
+
const { errorHandlerConfig, webhookAuth } = settings;
|
|
152
|
+
return {
|
|
153
|
+
parentTeams: settings.parentTeams,
|
|
154
|
+
inheritableSettings: {
|
|
155
|
+
zoneId: ownOrInherit(settings.zoneId),
|
|
156
|
+
errorHandlerConfig: errorHandlerConfig === undefined || errorHandlerConfig === null
|
|
157
|
+
? noneOrInherit(errorHandlerConfig)
|
|
158
|
+
: {
|
|
159
|
+
mode: 'OWN',
|
|
160
|
+
value: {
|
|
161
|
+
processId: await this.resolveSlugToId(errorHandlerConfig.processSlug),
|
|
162
|
+
tag: errorHandlerConfig.tag ?? null,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
webhookAuth: webhookAuth === undefined || webhookAuth === null
|
|
166
|
+
? noneOrInherit(webhookAuth)
|
|
167
|
+
: { mode: 'OWN', value: { headerName: webhookAuth.headerName ?? null, variableKey: webhookAuth.variableKey } },
|
|
168
|
+
primaryLocale: ownOrInherit(settings.primaryLocale),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
73
171
|
}
|
|
74
172
|
/**
|
|
75
173
|
* Writes the cloud's workspace versions and aliases into the settings as the settings.json
|
|
@@ -99,6 +197,25 @@ class SettingsRepository {
|
|
|
99
197
|
const match = Object.values(processes).find((process) => process.id === processId);
|
|
100
198
|
return match?.slug ?? processId;
|
|
101
199
|
}
|
|
200
|
+
/** Where each setting comes from; an API without it predates settings inheritance. */
|
|
201
|
+
originsOf(raw) {
|
|
202
|
+
if (!raw.origins) {
|
|
203
|
+
throw new errors_1.PlatformPredatesSettingsInheritance();
|
|
204
|
+
}
|
|
205
|
+
return raw.origins;
|
|
206
|
+
}
|
|
207
|
+
async resolveErrorHandler(raw) {
|
|
208
|
+
if (!raw.errorHandlerConfig?.processId) {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
processSlug: await this.resolveIdToSlug(raw.errorHandlerConfig.processId),
|
|
213
|
+
tag: raw.errorHandlerConfig.tag ?? null,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
parseString(raw) {
|
|
217
|
+
return typeof raw === 'string' && raw.length > 0 ? raw : undefined;
|
|
218
|
+
}
|
|
102
219
|
parseErrorHandler(raw) {
|
|
103
220
|
if (!raw || !raw.processSlug) {
|
|
104
221
|
return undefined;
|
|
@@ -194,7 +311,8 @@ class SettingsRepository {
|
|
|
194
311
|
if (settings.errorHandlerConfig !== undefined) {
|
|
195
312
|
file.errorHandlerConfig = settings.errorHandlerConfig;
|
|
196
313
|
}
|
|
197
|
-
// Last, so adding webhook auth to an existing settings.json is a purely additive diff.
|
|
314
|
+
// Last, so adding webhook auth to an existing settings.json is a purely additive diff. null
|
|
315
|
+
// (cleared) is written as is: absent means inherit.
|
|
198
316
|
if (settings.webhookAuth !== undefined) {
|
|
199
317
|
file.webhookAuth = settings.webhookAuth;
|
|
200
318
|
}
|
|
@@ -88,9 +88,9 @@ declare class FcodeGraphQLClient {
|
|
|
88
88
|
private mapFactorialSettings;
|
|
89
89
|
mapProcess({ id, slug, name, description, tags, script, readme, manifest, settings, triggers, resolvingTeamSlug, lifecycleRole, deletedAt, retainingVersionTags, }: any): Process;
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* undefined.
|
|
91
|
+
* The team's inheritable settings, each read from its `{ mode, value, from }`: the value in effect
|
|
92
|
+
* and where it comes from. An API that predates settings inheritance has no such field, and leaves
|
|
93
|
+
* `origins` undefined so the settings commands refuse instead of reading every setting as inherited.
|
|
94
94
|
*/
|
|
95
95
|
mapTeamSettings(team: any): RawTeamSettings;
|
|
96
96
|
mapTeamVersion(teamVersion: any): TeamVersion;
|
|
@@ -105,6 +105,11 @@ declare class FcodeGraphQLClient {
|
|
|
105
105
|
verifyTeamAccess(): Promise<void>;
|
|
106
106
|
team(): Promise<RawTeamSettings>;
|
|
107
107
|
pushTeam(update: TeamUpdateInput): Promise<RawTeamSettings>;
|
|
108
|
+
/**
|
|
109
|
+
* An API that predates settings inheritance rejects the query naming `inheritableSettings` as a
|
|
110
|
+
* schema error; that reads better as what it means than as GraphQL validation output.
|
|
111
|
+
*/
|
|
112
|
+
cleanTeamSettingsError(error: any): any;
|
|
108
113
|
mapProgrammingLanguageManifest(programmingLanguageManifest: any): ProgrammingLanguageManifest;
|
|
109
114
|
private mapDependencies;
|
|
110
115
|
/**
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const graphql_request_1 = require("graphql-request");
|
|
4
4
|
const types_1 = require("../core/types");
|
|
5
5
|
const locale_merger_1 = require("./locale-merger");
|
|
6
|
+
const cli_version_1 = require("../utils/cli-version");
|
|
7
|
+
const errors_1 = require("../core/errors");
|
|
6
8
|
const _ = require("lodash");
|
|
7
9
|
// Source code travels base64 encoded: Cloudflare's WAF blocks requests whose body
|
|
8
10
|
// looks like an attack payload, which source code often does. sourceCodeEncoding tells the
|
|
@@ -137,6 +139,17 @@ const PROCESS_FIELDS = `
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
`;
|
|
142
|
+
// Each inheritable setting of a team: its mode, the value in effect and the parent it comes from.
|
|
143
|
+
const INHERITABLE_SETTINGS_SELECTION = `
|
|
144
|
+
inheritableSettings {
|
|
145
|
+
zoneId { mode value from }
|
|
146
|
+
paramsSchemaValidationEnabled { mode value from }
|
|
147
|
+
errorHandlerConfig { mode value { processId tag } from }
|
|
148
|
+
webhookAuth { mode value { headerName variableKey } from }
|
|
149
|
+
primaryLocale { mode value from }
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
const UNKNOWN_FIELD_ERROR = /FieldUndefined|WrongType|is undefined|not present|not in type/;
|
|
140
153
|
const NETWORK_ERROR_CODES = ['ECONNREFUSED', 'ENOTFOUND', 'ECONNRESET', 'ETIMEDOUT', 'EAI_AGAIN'];
|
|
141
154
|
class FcodeGraphQLClient {
|
|
142
155
|
constructor(options) {
|
|
@@ -152,6 +165,7 @@ class FcodeGraphQLClient {
|
|
|
152
165
|
headers: {
|
|
153
166
|
'Fcode-Team': options.teamSlug,
|
|
154
167
|
authorization: `Bearer ${options.accessToken}`,
|
|
168
|
+
[cli_version_1.CLI_VERSION_HEADER]: (0, cli_version_1.cliVersion)(),
|
|
155
169
|
},
|
|
156
170
|
});
|
|
157
171
|
}
|
|
@@ -1005,10 +1019,9 @@ class FcodeGraphQLClient {
|
|
|
1005
1019
|
metadata.webhook = webhook;
|
|
1006
1020
|
}
|
|
1007
1021
|
if (settings?.formSettings) {
|
|
1022
|
+
// `appRole` is retired and push refuses it, so it is not written back: a role the cloud
|
|
1023
|
+
// already holds stays there, and `factorial.form.appTool` carries the user-facing one.
|
|
1008
1024
|
metadata.form = { enabled: Boolean(settings.formSettings.enabled) };
|
|
1009
|
-
if (settings.formSettings.appRole && settings.formSettings.appRole !== 'NONE') {
|
|
1010
|
-
metadata.form.appRole = settings.formSettings.appRole;
|
|
1011
|
-
}
|
|
1012
1025
|
if (settings.formSettings.authMode && settings.formSettings.authMode !== 'NONE') {
|
|
1013
1026
|
metadata.form.authMode = settings.formSettings.authMode;
|
|
1014
1027
|
}
|
|
@@ -1039,27 +1052,37 @@ class FcodeGraphQLClient {
|
|
|
1039
1052
|
return process;
|
|
1040
1053
|
}
|
|
1041
1054
|
/**
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
* undefined.
|
|
1055
|
+
* The team's inheritable settings, each read from its `{ mode, value, from }`: the value in effect
|
|
1056
|
+
* and where it comes from. An API that predates settings inheritance has no such field, and leaves
|
|
1057
|
+
* `origins` undefined so the settings commands refuse instead of reading every setting as inherited.
|
|
1045
1058
|
*/
|
|
1046
1059
|
mapTeamSettings(team) {
|
|
1047
1060
|
const settings = {
|
|
1048
1061
|
slug: team.slug,
|
|
1049
|
-
zoneId: team.zoneId,
|
|
1050
|
-
errorHandlerConfig: team.errorHandlerConfig,
|
|
1051
1062
|
parentTeams: team.parentTeams ?? [],
|
|
1052
1063
|
};
|
|
1053
|
-
const
|
|
1064
|
+
const inheritable = team.inheritableSettings;
|
|
1065
|
+
if (!inheritable) {
|
|
1066
|
+
return settings;
|
|
1067
|
+
}
|
|
1068
|
+
settings.zoneId = inheritable.zoneId?.value ?? undefined;
|
|
1069
|
+
settings.errorHandlerConfig = inheritable.errorHandlerConfig?.value ?? null;
|
|
1070
|
+
settings.primaryLocale = inheritable.primaryLocale?.value ?? null;
|
|
1071
|
+
const webhookAuth = inheritable.webhookAuth?.value;
|
|
1054
1072
|
if (webhookAuth?.variableKey) {
|
|
1055
1073
|
settings.webhookAuth = { variableKey: webhookAuth.variableKey };
|
|
1056
1074
|
if (webhookAuth.headerName && webhookAuth.headerName !== types_1.DEFAULT_WEBHOOK_AUTH_HEADER) {
|
|
1057
1075
|
settings.webhookAuth.headerName = webhookAuth.headerName;
|
|
1058
1076
|
}
|
|
1059
1077
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1078
|
+
const origin = (setting) => ({ mode: setting?.mode ?? 'INHERIT', from: setting?.from ?? null });
|
|
1079
|
+
settings.origins = {
|
|
1080
|
+
ZONE_ID: origin(inheritable.zoneId),
|
|
1081
|
+
PARAMS_SCHEMA_VALIDATION: origin(inheritable.paramsSchemaValidationEnabled),
|
|
1082
|
+
ERROR_HANDLER: origin(inheritable.errorHandlerConfig),
|
|
1083
|
+
WEBHOOK_AUTH: origin(inheritable.webhookAuth),
|
|
1084
|
+
PRIMARY_LOCALE: origin(inheritable.primaryLocale),
|
|
1085
|
+
};
|
|
1063
1086
|
return settings;
|
|
1064
1087
|
}
|
|
1065
1088
|
mapTeamVersion(teamVersion) {
|
|
@@ -1195,28 +1218,17 @@ class FcodeGraphQLClient {
|
|
|
1195
1218
|
query team($slug: ID!) {
|
|
1196
1219
|
team(slug: $slug) {
|
|
1197
1220
|
slug
|
|
1198
|
-
zoneId
|
|
1199
|
-
errorHandlerConfig {
|
|
1200
|
-
processId
|
|
1201
|
-
tag
|
|
1202
|
-
}
|
|
1203
1221
|
parentTeams {
|
|
1204
1222
|
slug
|
|
1205
1223
|
version
|
|
1206
1224
|
}
|
|
1207
|
-
|
|
1208
|
-
webhookAuth {
|
|
1209
|
-
headerName
|
|
1210
|
-
variableKey
|
|
1211
|
-
}
|
|
1212
|
-
primaryLocale
|
|
1213
|
-
}
|
|
1225
|
+
${INHERITABLE_SETTINGS_SELECTION}
|
|
1214
1226
|
}
|
|
1215
1227
|
}
|
|
1216
1228
|
`, { slug: this.teamSlug })
|
|
1217
1229
|
.then(({ team }) => this.mapTeamSettings(team))
|
|
1218
1230
|
.catch((error) => {
|
|
1219
|
-
return Promise.reject(this.
|
|
1231
|
+
return Promise.reject(this.cleanTeamSettingsError(error));
|
|
1220
1232
|
});
|
|
1221
1233
|
}
|
|
1222
1234
|
pushTeam(update) {
|
|
@@ -1225,30 +1237,32 @@ class FcodeGraphQLClient {
|
|
|
1225
1237
|
mutation updateTeam($slug: String!, $update: TeamUpdateInput!) {
|
|
1226
1238
|
updateTeam(slug: $slug, update: $update) {
|
|
1227
1239
|
slug
|
|
1228
|
-
zoneId
|
|
1229
|
-
errorHandlerConfig {
|
|
1230
|
-
processId
|
|
1231
|
-
tag
|
|
1232
|
-
}
|
|
1233
1240
|
parentTeams {
|
|
1234
1241
|
slug
|
|
1235
1242
|
version
|
|
1236
1243
|
}
|
|
1237
|
-
|
|
1238
|
-
webhookAuth {
|
|
1239
|
-
headerName
|
|
1240
|
-
variableKey
|
|
1241
|
-
}
|
|
1242
|
-
primaryLocale
|
|
1243
|
-
}
|
|
1244
|
+
${INHERITABLE_SETTINGS_SELECTION}
|
|
1244
1245
|
}
|
|
1245
1246
|
}
|
|
1246
1247
|
`, { slug: this.teamSlug, update })
|
|
1247
1248
|
.then(({ updateTeam: team }) => this.mapTeamSettings(team))
|
|
1248
1249
|
.catch((error) => {
|
|
1249
|
-
return Promise.reject(this.
|
|
1250
|
+
return Promise.reject(this.cleanTeamSettingsError(error));
|
|
1250
1251
|
});
|
|
1251
1252
|
}
|
|
1253
|
+
/**
|
|
1254
|
+
* An API that predates settings inheritance rejects the query naming `inheritableSettings` as a
|
|
1255
|
+
* schema error; that reads better as what it means than as GraphQL validation output.
|
|
1256
|
+
*/
|
|
1257
|
+
cleanTeamSettingsError(error) {
|
|
1258
|
+
const message = error?.response?.errors?.[0]?.message ?? '';
|
|
1259
|
+
// Only the schema errors an older API answers with (an unknown output field, an unknown input
|
|
1260
|
+
// field): the API's own validation errors also name inheritableSettings, and mean something else.
|
|
1261
|
+
if (message.includes('inheritableSettings') && UNKNOWN_FIELD_ERROR.test(message)) {
|
|
1262
|
+
return new errors_1.PlatformPredatesSettingsInheritance();
|
|
1263
|
+
}
|
|
1264
|
+
return this.cleanError(error);
|
|
1265
|
+
}
|
|
1252
1266
|
mapProgrammingLanguageManifest(programmingLanguageManifest) {
|
|
1253
1267
|
const allDependencies = programmingLanguageManifest.nextInstallation?.dependencies || programmingLanguageManifest.dependencies;
|
|
1254
1268
|
return new types_1.ProgrammingLanguageManifest(programmingLanguageManifest.id, programmingLanguageManifest.programmingLanguage, programmingLanguageManifest.programmingLanguage, this.mapDependencies(allDependencies),
|
|
@@ -71,16 +71,20 @@ function readLocale(workspaceDir, locale) {
|
|
|
71
71
|
...readLocaleFile(workspaceDir, locale),
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
function
|
|
74
|
+
function primaryLocaleIn(workspaceDir, fileName) {
|
|
75
75
|
try {
|
|
76
76
|
(0, settings_1.migrateLegacySettingsFile)(workspaceDir);
|
|
77
|
-
const settings = fs.readJsonSync(path.join(workspaceDir,
|
|
77
|
+
const settings = fs.readJsonSync(path.join(workspaceDir, fileName));
|
|
78
78
|
return typeof settings?.primaryLocale === 'string' && settings.primaryLocale.length > 0 ? settings.primaryLocale : undefined;
|
|
79
79
|
}
|
|
80
80
|
catch {
|
|
81
81
|
return undefined;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
/** The workspace's own choice, or the one it inherits when settings.json makes none. */
|
|
85
|
+
function chosenPrimaryLocaleOf(workspaceDir) {
|
|
86
|
+
return primaryLocaleIn(workspaceDir, settings_1.SETTINGS_FILENAME) ?? primaryLocaleIn(workspaceDir, settings_1.INHERITED_SETTINGS_FILENAME);
|
|
87
|
+
}
|
|
84
88
|
/** Every locale file in the workspace, own and inherited: `pull` writes them all to `i18n/`. */
|
|
85
89
|
function localeFileSlugs(workspaceDir) {
|
|
86
90
|
const directory = path.join(workspaceDir, I18N_DIRECTORY);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Process } from '../core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Refuses to push a process that still declares `form.appRole`.
|
|
4
|
+
*
|
|
5
|
+
* The role is retired: a lifecycle form is named by its reserved slug, and the form offered from
|
|
6
|
+
* the app's page is `factorial.form.appTool`. Writing the role kept agents copying it into new
|
|
7
|
+
* Apps, where it beats the slug and, set by `appTool`, took the install role away. A process that
|
|
8
|
+
* already holds a role in the cloud keeps it when the line is deleted, since a push only sends the
|
|
9
|
+
* role when metadata.json names it — so legacy Apps need no rename to push again.
|
|
10
|
+
*
|
|
11
|
+
* Inherited and deleted processes are not pushed, so they are not checked.
|
|
12
|
+
*
|
|
13
|
+
* @returns the message to fail the push with, or undefined when no process declares a role.
|
|
14
|
+
*/
|
|
15
|
+
export declare function retiredAppRoleError(processes: Array<Process>): string | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.retiredAppRoleError = retiredAppRoleError;
|
|
4
|
+
const types_1 = require("../core/types");
|
|
5
|
+
const LIFECYCLE_SLUGS = ['install', 'settings', 'uninstall'];
|
|
6
|
+
/**
|
|
7
|
+
* Refuses to push a process that still declares `form.appRole`.
|
|
8
|
+
*
|
|
9
|
+
* The role is retired: a lifecycle form is named by its reserved slug, and the form offered from
|
|
10
|
+
* the app's page is `factorial.form.appTool`. Writing the role kept agents copying it into new
|
|
11
|
+
* Apps, where it beats the slug and, set by `appTool`, took the install role away. A process that
|
|
12
|
+
* already holds a role in the cloud keeps it when the line is deleted, since a push only sends the
|
|
13
|
+
* role when metadata.json names it — so legacy Apps need no rename to push again.
|
|
14
|
+
*
|
|
15
|
+
* Inherited and deleted processes are not pushed, so they are not checked.
|
|
16
|
+
*
|
|
17
|
+
* @returns the message to fail the push with, or undefined when no process declares a role.
|
|
18
|
+
*/
|
|
19
|
+
function retiredAppRoleError(processes) {
|
|
20
|
+
const offenders = processes.filter((process) => !(0, types_1.isReadOnly)(process) && process.form?.appRole && process.form.appRole !== 'NONE');
|
|
21
|
+
if (offenders.length === 0) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const lines = offenders.map((process) => ` - processes/${process.slug}: ${fixFor(process)}`);
|
|
25
|
+
return [
|
|
26
|
+
'`form.appRole` is no longer supported. Remove it from metadata.json:',
|
|
27
|
+
...lines,
|
|
28
|
+
`Install, settings and uninstall forms are the processes named ${LIFECYCLE_SLUGS.map((slug) => `'${slug}'`).join(', ')}.`,
|
|
29
|
+
].join('\n');
|
|
30
|
+
}
|
|
31
|
+
function fixFor(process) {
|
|
32
|
+
const role = process.form.appRole;
|
|
33
|
+
if (role === 'USER_FACING_FORM') {
|
|
34
|
+
return 'set `factorial.form.appTool: true` instead.';
|
|
35
|
+
}
|
|
36
|
+
const slug = role.toLowerCase();
|
|
37
|
+
if (process.slug === slug) {
|
|
38
|
+
return `the '${slug}' slug already makes it the ${slug} form.`;
|
|
39
|
+
}
|
|
40
|
+
if (process.id) {
|
|
41
|
+
return `the cloud keeps the ${role} role it already has once the line is gone.`;
|
|
42
|
+
}
|
|
43
|
+
return `rename the process to '${slug}'.`;
|
|
44
|
+
}
|
|
@@ -13,9 +13,11 @@ export type WebhookAuthResult = {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function findHeader(headers: http.IncomingHttpHeaders, name: string): string | undefined;
|
|
15
15
|
/**
|
|
16
|
-
* The configuration a webhook actually enforces: its own when CUSTOM, the workspace's when TEAM.
|
|
16
|
+
* The configuration a webhook actually enforces: its own when CUSTOM, the workspace's when TEAM. The
|
|
17
|
+
* workspace's is its own (settings.json) or, when settings.json leaves it out, the inherited one
|
|
18
|
+
* (settings.inherited.json); null in settings.json clears it, a parent's included.
|
|
17
19
|
* Returns null only when the webhook is public — TEAM without a workspace configuration throws,
|
|
18
20
|
* because treating it as public would silently unprotect a webhook its owner chose to protect.
|
|
19
21
|
*/
|
|
20
|
-
export declare function resolveEffectiveAuth(webhook: ProcessWebhook | undefined, teamWebhookAuth: WebhookAuth | undefined): WebhookAuth | null;
|
|
22
|
+
export declare function resolveEffectiveAuth(webhook: ProcessWebhook | undefined, teamWebhookAuth: WebhookAuth | null | undefined, inheritedTeamWebhookAuth?: WebhookAuth): WebhookAuth | null;
|
|
21
23
|
export declare function checkWebhookAuth(effectiveAuth: WebhookAuth | null, headers: http.IncomingHttpHeaders, variableValue: string | undefined): WebhookAuthResult;
|
|
@@ -26,20 +26,26 @@ function findHeader(headers, name) {
|
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* The configuration a webhook actually enforces: its own when CUSTOM, the workspace's when TEAM.
|
|
29
|
+
* The configuration a webhook actually enforces: its own when CUSTOM, the workspace's when TEAM. The
|
|
30
|
+
* workspace's is its own (settings.json) or, when settings.json leaves it out, the inherited one
|
|
31
|
+
* (settings.inherited.json); null in settings.json clears it, a parent's included.
|
|
30
32
|
* Returns null only when the webhook is public — TEAM without a workspace configuration throws,
|
|
31
33
|
* because treating it as public would silently unprotect a webhook its owner chose to protect.
|
|
32
34
|
*/
|
|
33
|
-
function resolveEffectiveAuth(webhook, teamWebhookAuth) {
|
|
35
|
+
function resolveEffectiveAuth(webhook, teamWebhookAuth, inheritedTeamWebhookAuth) {
|
|
34
36
|
const authMode = webhook?.authMode;
|
|
35
37
|
if (!authMode || authMode === 'NONE') {
|
|
36
38
|
return null;
|
|
37
39
|
}
|
|
38
40
|
if (authMode === 'TEAM') {
|
|
39
|
-
if (
|
|
40
|
-
throw new Error('this webhook inherits the workspace webhook auth, which
|
|
41
|
+
if (teamWebhookAuth === null) {
|
|
42
|
+
throw new Error('this webhook inherits the workspace webhook auth, which settings.json sets to none');
|
|
41
43
|
}
|
|
42
|
-
|
|
44
|
+
const workspaceAuth = teamWebhookAuth ?? inheritedTeamWebhookAuth;
|
|
45
|
+
if (!workspaceAuth) {
|
|
46
|
+
throw new Error('this webhook inherits the workspace webhook auth, which is not set in settings.json nor inherited');
|
|
47
|
+
}
|
|
48
|
+
return workspaceAuth;
|
|
43
49
|
}
|
|
44
50
|
if (!webhook?.auth?.variableKey) {
|
|
45
51
|
throw new Error('this webhook uses the CUSTOM auth mode but names no variable in metadata.json');
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The header the CLI reports its version in, so the platform can refuse a CLI too old for a request. */
|
|
2
|
+
export declare const CLI_VERSION_HEADER = "Fcode-Cli-Version";
|
|
3
|
+
/**
|
|
4
|
+
* The installed CLI's own version, read from its package.json: two levels above this file both in
|
|
5
|
+
* src/utils (tests) and dist/utils (the published package, which always ships package.json).
|
|
6
|
+
*/
|
|
7
|
+
export declare function cliVersion(): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLI_VERSION_HEADER = void 0;
|
|
4
|
+
exports.cliVersion = cliVersion;
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
/** The header the CLI reports its version in, so the platform can refuse a CLI too old for a request. */
|
|
8
|
+
exports.CLI_VERSION_HEADER = 'Fcode-Cli-Version';
|
|
9
|
+
let cached;
|
|
10
|
+
/**
|
|
11
|
+
* The installed CLI's own version, read from its package.json: two levels above this file both in
|
|
12
|
+
* src/utils (tests) and dist/utils (the published package, which always ships package.json).
|
|
13
|
+
*/
|
|
14
|
+
function cliVersion() {
|
|
15
|
+
if (cached === undefined) {
|
|
16
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf8'));
|
|
17
|
+
cached = String(packageJson.version);
|
|
18
|
+
}
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
@@ -16,6 +16,7 @@ export declare function renderGitignoreWithInherited(existing: string, inherited
|
|
|
16
16
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
17
17
|
* Inherited variable values live in one file, which is ignored as soon as the
|
|
18
18
|
* workspace inherits any variable; inherited locales are one file each, like modules;
|
|
19
|
-
* inherited dependencies are one file per language, like variables
|
|
19
|
+
* inherited dependencies are one file per language, like variables; inherited settings are one
|
|
20
|
+
* file.
|
|
20
21
|
*/
|
|
21
22
|
export declare function syncInheritedGitignore(workspaceDir?: string): Promise<void>;
|
package/dist/utils/gitignore.js
CHANGED
|
@@ -9,6 +9,7 @@ const workspace_1 = require("../config/workspace");
|
|
|
9
9
|
const global_1 = require("../config/global");
|
|
10
10
|
const settings_1 = require("../config/settings");
|
|
11
11
|
const programming_language_manifest_1 = require("../repositories/programming-language-manifest");
|
|
12
|
+
const settings_2 = require("../repositories/settings");
|
|
12
13
|
const filenamify_1 = require("./filenamify");
|
|
13
14
|
const file_utils_1 = require("./file-utils");
|
|
14
15
|
exports.INHERITED_BLOCK_START = '# BEGIN fcode inherited resources (auto-generated, do not edit)';
|
|
@@ -51,7 +52,8 @@ function stripManagedBlock(content) {
|
|
|
51
52
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
52
53
|
* Inherited variable values live in one file, which is ignored as soon as the
|
|
53
54
|
* workspace inherits any variable; inherited locales are one file each, like modules;
|
|
54
|
-
* inherited dependencies are one file per language, like variables
|
|
55
|
+
* inherited dependencies are one file per language, like variables; inherited settings are one
|
|
56
|
+
* file.
|
|
55
57
|
*/
|
|
56
58
|
async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
57
59
|
const team = await new workspace_1.default(workspaceDir).getActiveTeam();
|
|
@@ -71,6 +73,8 @@ async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
|
71
73
|
// inherit — checked on disk because the manifest metadata tracks the workspace's own list, not
|
|
72
74
|
// what its parents contribute.
|
|
73
75
|
...exports.INHERITED_DEPENDENCY_FILES.filter((file) => fs.existsSync(path.join(workspaceDir, file))),
|
|
76
|
+
// Written only when the workspace inherits a setting, so checked on disk like dependencies.
|
|
77
|
+
...(fs.existsSync(path.join(workspaceDir, settings_2.INHERITED_SETTINGS_FILENAME)) ? [settings_2.INHERITED_SETTINGS_FILENAME] : []),
|
|
74
78
|
];
|
|
75
79
|
const gitignorePath = path.join(workspaceDir, '.gitignore');
|
|
76
80
|
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf8') : '';
|
package/oclif.manifest.json
CHANGED