@factorialco/fcode-cli 3.5.0 → 3.6.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 +1 -1
- package/dist/commands/http.d.ts +2 -1
- package/dist/commands/http.js +4 -2
- 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/executors/javascript-execution-task.js +21 -8
- package/dist/core/types.d.ts +40 -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/runtime/libs/python/fcode/context.py +28 -8
- package/dist/services/fcode-graphql-client.d.ts +8 -3
- package/dist/services/fcode-graphql-client.js +98 -48
- package/dist/services/locale-resolver.d.ts +6 -2
- package/dist/services/locale-resolver.js +35 -8
- 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
|
@@ -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
|
}
|
|
@@ -96,6 +96,7 @@ class I18n:
|
|
|
96
96
|
payload = i18n_payload or {}
|
|
97
97
|
self.__translations = payload.get("translations") or {}
|
|
98
98
|
self.__by_locale = payload.get("byLocale") or {}
|
|
99
|
+
self.__primary_locale = payload.get("primaryLocale")
|
|
99
100
|
self.locale = payload.get("locale")
|
|
100
101
|
|
|
101
102
|
def __call__(self, key, args=None, options=None):
|
|
@@ -125,24 +126,43 @@ class I18n:
|
|
|
125
126
|
"""The maps a requested locale opens, most specific first.
|
|
126
127
|
|
|
127
128
|
An identifier no locale matches is shortened one segment at a time ("en-gb" reads "en"),
|
|
128
|
-
by name first and ignoring case only as a last resort
|
|
129
|
-
|
|
129
|
+
by name first and ignoring case only as a last resort, and a language that still matches
|
|
130
|
+
nothing is broadened to a regional locale of it ("es" reads "es-ES", the primary one if it
|
|
131
|
+
is among them, else the alphabetically first) -- the same chain the cloud executors
|
|
132
|
+
resolve, so a local run answers the same identifiers the same way.
|
|
130
133
|
"""
|
|
131
134
|
if locale is None or not self.__by_locale:
|
|
132
135
|
return []
|
|
136
|
+
import re
|
|
137
|
+
|
|
133
138
|
names = list(self.__by_locale.keys())
|
|
134
139
|
chain = []
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
|
|
141
|
+
def matching(candidate):
|
|
137
142
|
if candidate in names:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
return candidate
|
|
144
|
+
folded = sorted(n for n in names if n.lower() == candidate.lower())
|
|
145
|
+
return folded[0] if folded else None
|
|
146
|
+
|
|
147
|
+
def add(match):
|
|
142
148
|
if match is not None and match not in chain:
|
|
143
149
|
chain.append(match)
|
|
150
|
+
|
|
151
|
+
candidate = locale
|
|
152
|
+
language = None
|
|
153
|
+
while candidate:
|
|
154
|
+
add(matching(candidate))
|
|
155
|
+
language = candidate
|
|
144
156
|
last_separator = max(candidate.rfind("-"), candidate.rfind("_"))
|
|
145
157
|
candidate = candidate[:last_separator] if last_separator > 0 else None
|
|
158
|
+
if language and not re.search(r"[-_]", language) and matching(language) is None:
|
|
159
|
+
same_language = sorted(
|
|
160
|
+
n for n in names if re.split(r"[-_]", n)[0].lower() == language.lower()
|
|
161
|
+
)
|
|
162
|
+
if self.__primary_locale in same_language:
|
|
163
|
+
add(self.__primary_locale)
|
|
164
|
+
elif same_language:
|
|
165
|
+
add(same_language[0])
|
|
146
166
|
return [self.__by_locale[name] for name in chain if self.__by_locale[name]]
|
|
147
167
|
|
|
148
168
|
|
|
@@ -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,7 +3,14 @@ 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");
|
|
9
|
+
// Source code travels base64 encoded: Cloudflare's WAF blocks requests whose body
|
|
10
|
+
// looks like an attack payload, which source code often does. sourceCodeEncoding tells the
|
|
11
|
+
// API to decode it back.
|
|
12
|
+
const SOURCE_CODE_ENCODING = 'BASE64';
|
|
13
|
+
const encodeSourceCode = (sourceCode) => sourceCode == null ? sourceCode : Buffer.from(sourceCode, 'utf8').toString('base64');
|
|
7
14
|
// Manifest fields an fcode-api older than this CLI may not know. The manifest query falls back to a
|
|
8
15
|
// narrower selection when the error names one of them, so a CLI stays usable against an
|
|
9
16
|
// on-premises api that has not been updated yet.
|
|
@@ -132,6 +139,17 @@ const PROCESS_FIELDS = `
|
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
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/;
|
|
135
153
|
const NETWORK_ERROR_CODES = ['ECONNREFUSED', 'ENOTFOUND', 'ECONNRESET', 'ETIMEDOUT', 'EAI_AGAIN'];
|
|
136
154
|
class FcodeGraphQLClient {
|
|
137
155
|
constructor(options) {
|
|
@@ -147,6 +165,7 @@ class FcodeGraphQLClient {
|
|
|
147
165
|
headers: {
|
|
148
166
|
'Fcode-Team': options.teamSlug,
|
|
149
167
|
authorization: `Bearer ${options.accessToken}`,
|
|
168
|
+
[cli_version_1.CLI_VERSION_HEADER]: (0, cli_version_1.cliVersion)(),
|
|
150
169
|
},
|
|
151
170
|
});
|
|
152
171
|
}
|
|
@@ -261,8 +280,19 @@ class FcodeGraphQLClient {
|
|
|
261
280
|
createVersionedProcesses(versionedProcess) {
|
|
262
281
|
return this.client
|
|
263
282
|
.request((0, graphql_request_1.gql) `
|
|
264
|
-
mutation publishProcess(
|
|
265
|
-
|
|
283
|
+
mutation publishProcess(
|
|
284
|
+
$processId: ID!
|
|
285
|
+
$tag: String!
|
|
286
|
+
$sourceCode: String
|
|
287
|
+
$sourceCodeEncoding: SourceCodeEncoding
|
|
288
|
+
$parametersSchema: JSON
|
|
289
|
+
$readme: String
|
|
290
|
+
) {
|
|
291
|
+
publishProcess(
|
|
292
|
+
processId: $processId
|
|
293
|
+
tag: $tag
|
|
294
|
+
input: { sourceCode: $sourceCode, sourceCodeEncoding: $sourceCodeEncoding, parametersSchema: $parametersSchema, readme: $readme }
|
|
295
|
+
) {
|
|
266
296
|
id
|
|
267
297
|
}
|
|
268
298
|
}
|
|
@@ -271,7 +301,8 @@ class FcodeGraphQLClient {
|
|
|
271
301
|
tag: versionedProcess.tag,
|
|
272
302
|
parametersSchema: versionedProcess.script.parametersSchema,
|
|
273
303
|
readme: versionedProcess.readme,
|
|
274
|
-
sourceCode: versionedProcess.script.sourceCode,
|
|
304
|
+
sourceCode: encodeSourceCode(versionedProcess.script.sourceCode),
|
|
305
|
+
sourceCodeEncoding: SOURCE_CODE_ENCODING,
|
|
275
306
|
})
|
|
276
307
|
.catch((error) => {
|
|
277
308
|
return Promise.reject(this.cleanError(error));
|
|
@@ -330,15 +361,20 @@ class FcodeGraphQLClient {
|
|
|
330
361
|
createVersionedScriptLibrary(versionedScriptLibrary) {
|
|
331
362
|
return this.client
|
|
332
363
|
.request((0, graphql_request_1.gql) `
|
|
333
|
-
mutation publishScriptLibrary($scriptLibraryId: ID!, $tag: String!, $sourceCode: String) {
|
|
334
|
-
publishScriptLibrary(
|
|
364
|
+
mutation publishScriptLibrary($scriptLibraryId: ID!, $tag: String!, $sourceCode: String, $sourceCodeEncoding: SourceCodeEncoding) {
|
|
365
|
+
publishScriptLibrary(
|
|
366
|
+
scriptLibraryId: $scriptLibraryId
|
|
367
|
+
tag: $tag
|
|
368
|
+
input: { sourceCode: $sourceCode, sourceCodeEncoding: $sourceCodeEncoding }
|
|
369
|
+
) {
|
|
335
370
|
id
|
|
336
371
|
}
|
|
337
372
|
}
|
|
338
373
|
`, {
|
|
339
374
|
scriptLibraryId: versionedScriptLibrary.id,
|
|
340
375
|
tag: versionedScriptLibrary.tag,
|
|
341
|
-
sourceCode: versionedScriptLibrary.sourceCode,
|
|
376
|
+
sourceCode: encodeSourceCode(versionedScriptLibrary.sourceCode),
|
|
377
|
+
sourceCodeEncoding: SOURCE_CODE_ENCODING,
|
|
342
378
|
})
|
|
343
379
|
.catch((error) => {
|
|
344
380
|
return Promise.reject(this.cleanError(error));
|
|
@@ -553,7 +589,8 @@ class FcodeGraphQLClient {
|
|
|
553
589
|
parametersSchema: process.script.parametersSchema,
|
|
554
590
|
readme: process.readme,
|
|
555
591
|
tags: process.tags,
|
|
556
|
-
sourceCode: process.script.sourceCode,
|
|
592
|
+
sourceCode: encodeSourceCode(process.script.sourceCode),
|
|
593
|
+
sourceCodeEncoding: SOURCE_CODE_ENCODING,
|
|
557
594
|
manifest: process.settings.dependencies.scopedToProcess ? process.manifest : null,
|
|
558
595
|
settings,
|
|
559
596
|
};
|
|
@@ -640,8 +677,8 @@ class FcodeGraphQLClient {
|
|
|
640
677
|
if (scriptLibrary.id) {
|
|
641
678
|
return this.client
|
|
642
679
|
.request((0, graphql_request_1.gql) `
|
|
643
|
-
mutation updateScriptLibrary($id: ID!, $sourceCode: String) {
|
|
644
|
-
updateScriptLibrary(id: $id, input: { sourceCode: $sourceCode }) {
|
|
680
|
+
mutation updateScriptLibrary($id: ID!, $sourceCode: String, $sourceCodeEncoding: SourceCodeEncoding) {
|
|
681
|
+
updateScriptLibrary(id: $id, input: { sourceCode: $sourceCode, sourceCodeEncoding: $sourceCodeEncoding }) {
|
|
645
682
|
id
|
|
646
683
|
name
|
|
647
684
|
programmingLanguage
|
|
@@ -650,7 +687,8 @@ class FcodeGraphQLClient {
|
|
|
650
687
|
}
|
|
651
688
|
`, {
|
|
652
689
|
id: scriptLibrary.id,
|
|
653
|
-
sourceCode: scriptLibrary.sourceCode,
|
|
690
|
+
sourceCode: encodeSourceCode(scriptLibrary.sourceCode),
|
|
691
|
+
sourceCodeEncoding: SOURCE_CODE_ENCODING,
|
|
654
692
|
})
|
|
655
693
|
.then(({ updateScriptLibrary: scriptLibrary }) => new types_1.ScriptLibrary(scriptLibrary.id, scriptLibrary.name, scriptLibrary.sourceCode, scriptLibrary.programmingLanguage))
|
|
656
694
|
.catch((error) => {
|
|
@@ -659,8 +697,18 @@ class FcodeGraphQLClient {
|
|
|
659
697
|
}
|
|
660
698
|
return this.client
|
|
661
699
|
.request((0, graphql_request_1.gql) `
|
|
662
|
-
mutation createScriptLibrary(
|
|
663
|
-
|
|
700
|
+
mutation createScriptLibrary(
|
|
701
|
+
$name: String!
|
|
702
|
+
$sourceCode: String
|
|
703
|
+
$sourceCodeEncoding: SourceCodeEncoding
|
|
704
|
+
$programmingLanguage: ProgrammingLanguage
|
|
705
|
+
) {
|
|
706
|
+
createScriptLibrary(
|
|
707
|
+
name: $name
|
|
708
|
+
programmingLanguage: $programmingLanguage
|
|
709
|
+
sourceCode: $sourceCode
|
|
710
|
+
sourceCodeEncoding: $sourceCodeEncoding
|
|
711
|
+
) {
|
|
664
712
|
id
|
|
665
713
|
name
|
|
666
714
|
sourceCode
|
|
@@ -669,7 +717,8 @@ class FcodeGraphQLClient {
|
|
|
669
717
|
}
|
|
670
718
|
`, {
|
|
671
719
|
name: scriptLibrary.name,
|
|
672
|
-
sourceCode: scriptLibrary.sourceCode,
|
|
720
|
+
sourceCode: encodeSourceCode(scriptLibrary.sourceCode),
|
|
721
|
+
sourceCodeEncoding: SOURCE_CODE_ENCODING,
|
|
673
722
|
programmingLanguage: scriptLibrary.programmingLanguage,
|
|
674
723
|
})
|
|
675
724
|
.then(({ createScriptLibrary: scriptLibrary }) => new types_1.ScriptLibrary(scriptLibrary.id, scriptLibrary.name, scriptLibrary.sourceCode, scriptLibrary.programmingLanguage))
|
|
@@ -1004,27 +1053,37 @@ class FcodeGraphQLClient {
|
|
|
1004
1053
|
return process;
|
|
1005
1054
|
}
|
|
1006
1055
|
/**
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
* undefined.
|
|
1056
|
+
* The team's inheritable settings, each read from its `{ mode, value, from }`: the value in effect
|
|
1057
|
+
* and where it comes from. An API that predates settings inheritance has no such field, and leaves
|
|
1058
|
+
* `origins` undefined so the settings commands refuse instead of reading every setting as inherited.
|
|
1010
1059
|
*/
|
|
1011
1060
|
mapTeamSettings(team) {
|
|
1012
1061
|
const settings = {
|
|
1013
1062
|
slug: team.slug,
|
|
1014
|
-
zoneId: team.zoneId,
|
|
1015
|
-
errorHandlerConfig: team.errorHandlerConfig,
|
|
1016
1063
|
parentTeams: team.parentTeams ?? [],
|
|
1017
1064
|
};
|
|
1018
|
-
const
|
|
1065
|
+
const inheritable = team.inheritableSettings;
|
|
1066
|
+
if (!inheritable) {
|
|
1067
|
+
return settings;
|
|
1068
|
+
}
|
|
1069
|
+
settings.zoneId = inheritable.zoneId?.value ?? undefined;
|
|
1070
|
+
settings.errorHandlerConfig = inheritable.errorHandlerConfig?.value ?? null;
|
|
1071
|
+
settings.primaryLocale = inheritable.primaryLocale?.value ?? null;
|
|
1072
|
+
const webhookAuth = inheritable.webhookAuth?.value;
|
|
1019
1073
|
if (webhookAuth?.variableKey) {
|
|
1020
1074
|
settings.webhookAuth = { variableKey: webhookAuth.variableKey };
|
|
1021
1075
|
if (webhookAuth.headerName && webhookAuth.headerName !== types_1.DEFAULT_WEBHOOK_AUTH_HEADER) {
|
|
1022
1076
|
settings.webhookAuth.headerName = webhookAuth.headerName;
|
|
1023
1077
|
}
|
|
1024
1078
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1079
|
+
const origin = (setting) => ({ mode: setting?.mode ?? 'INHERIT', from: setting?.from ?? null });
|
|
1080
|
+
settings.origins = {
|
|
1081
|
+
ZONE_ID: origin(inheritable.zoneId),
|
|
1082
|
+
PARAMS_SCHEMA_VALIDATION: origin(inheritable.paramsSchemaValidationEnabled),
|
|
1083
|
+
ERROR_HANDLER: origin(inheritable.errorHandlerConfig),
|
|
1084
|
+
WEBHOOK_AUTH: origin(inheritable.webhookAuth),
|
|
1085
|
+
PRIMARY_LOCALE: origin(inheritable.primaryLocale),
|
|
1086
|
+
};
|
|
1028
1087
|
return settings;
|
|
1029
1088
|
}
|
|
1030
1089
|
mapTeamVersion(teamVersion) {
|
|
@@ -1160,28 +1219,17 @@ class FcodeGraphQLClient {
|
|
|
1160
1219
|
query team($slug: ID!) {
|
|
1161
1220
|
team(slug: $slug) {
|
|
1162
1221
|
slug
|
|
1163
|
-
zoneId
|
|
1164
|
-
errorHandlerConfig {
|
|
1165
|
-
processId
|
|
1166
|
-
tag
|
|
1167
|
-
}
|
|
1168
1222
|
parentTeams {
|
|
1169
1223
|
slug
|
|
1170
1224
|
version
|
|
1171
1225
|
}
|
|
1172
|
-
|
|
1173
|
-
webhookAuth {
|
|
1174
|
-
headerName
|
|
1175
|
-
variableKey
|
|
1176
|
-
}
|
|
1177
|
-
primaryLocale
|
|
1178
|
-
}
|
|
1226
|
+
${INHERITABLE_SETTINGS_SELECTION}
|
|
1179
1227
|
}
|
|
1180
1228
|
}
|
|
1181
1229
|
`, { slug: this.teamSlug })
|
|
1182
1230
|
.then(({ team }) => this.mapTeamSettings(team))
|
|
1183
1231
|
.catch((error) => {
|
|
1184
|
-
return Promise.reject(this.
|
|
1232
|
+
return Promise.reject(this.cleanTeamSettingsError(error));
|
|
1185
1233
|
});
|
|
1186
1234
|
}
|
|
1187
1235
|
pushTeam(update) {
|
|
@@ -1190,30 +1238,32 @@ class FcodeGraphQLClient {
|
|
|
1190
1238
|
mutation updateTeam($slug: String!, $update: TeamUpdateInput!) {
|
|
1191
1239
|
updateTeam(slug: $slug, update: $update) {
|
|
1192
1240
|
slug
|
|
1193
|
-
zoneId
|
|
1194
|
-
errorHandlerConfig {
|
|
1195
|
-
processId
|
|
1196
|
-
tag
|
|
1197
|
-
}
|
|
1198
1241
|
parentTeams {
|
|
1199
1242
|
slug
|
|
1200
1243
|
version
|
|
1201
1244
|
}
|
|
1202
|
-
|
|
1203
|
-
webhookAuth {
|
|
1204
|
-
headerName
|
|
1205
|
-
variableKey
|
|
1206
|
-
}
|
|
1207
|
-
primaryLocale
|
|
1208
|
-
}
|
|
1245
|
+
${INHERITABLE_SETTINGS_SELECTION}
|
|
1209
1246
|
}
|
|
1210
1247
|
}
|
|
1211
1248
|
`, { slug: this.teamSlug, update })
|
|
1212
1249
|
.then(({ updateTeam: team }) => this.mapTeamSettings(team))
|
|
1213
1250
|
.catch((error) => {
|
|
1214
|
-
return Promise.reject(this.
|
|
1251
|
+
return Promise.reject(this.cleanTeamSettingsError(error));
|
|
1215
1252
|
});
|
|
1216
1253
|
}
|
|
1254
|
+
/**
|
|
1255
|
+
* An API that predates settings inheritance rejects the query naming `inheritableSettings` as a
|
|
1256
|
+
* schema error; that reads better as what it means than as GraphQL validation output.
|
|
1257
|
+
*/
|
|
1258
|
+
cleanTeamSettingsError(error) {
|
|
1259
|
+
const message = error?.response?.errors?.[0]?.message ?? '';
|
|
1260
|
+
// Only the schema errors an older API answers with (an unknown output field, an unknown input
|
|
1261
|
+
// field): the API's own validation errors also name inheritableSettings, and mean something else.
|
|
1262
|
+
if (message.includes('inheritableSettings') && UNKNOWN_FIELD_ERROR.test(message)) {
|
|
1263
|
+
return new errors_1.PlatformPredatesSettingsInheritance();
|
|
1264
|
+
}
|
|
1265
|
+
return this.cleanError(error);
|
|
1266
|
+
}
|
|
1217
1267
|
mapProgrammingLanguageManifest(programmingLanguageManifest) {
|
|
1218
1268
|
const allDependencies = programmingLanguageManifest.nextInstallation?.dependencies || programmingLanguageManifest.dependencies;
|
|
1219
1269
|
return new types_1.ProgrammingLanguageManifest(programmingLanguageManifest.id, programmingLanguageManifest.programmingLanguage, programmingLanguageManifest.programmingLanguage, this.mapDependencies(allDependencies),
|
|
@@ -16,7 +16,9 @@ export declare function flattenTranslations(document: unknown, prefix?: string,
|
|
|
16
16
|
* publishes plain languages, so the request degrades one segment at a time — split on `-` or `_`,
|
|
17
17
|
* without parsing a language tag, since any short slug is a legal identifier here — and every step
|
|
18
18
|
* that matches stays in the chain: a variant file translates the keys that differ and leaves the
|
|
19
|
-
* rest to the language it came from.
|
|
19
|
+
* rest to the language it came from. The opposite happens too — the host sends `es` while the
|
|
20
|
+
* workspace has `es-ES` — so a language layer that matches nothing, by name or ignoring case, is
|
|
21
|
+
* broadened to a regional file of that language, in the place the language would have taken.
|
|
20
22
|
*/
|
|
21
23
|
export declare function localeFallbackChain(requested: string | undefined, primary: string | undefined, available: Array<string>): Array<string>;
|
|
22
24
|
/**
|
|
@@ -25,10 +27,12 @@ export declare function localeFallbackChain(requested: string | undefined, prima
|
|
|
25
27
|
* shorter form of the requested one the workspace publishes — layered underneath, plus each
|
|
26
28
|
* locale's own map (inherited file layered under its own, but NOT merged over the primary) so a
|
|
27
29
|
* call naming a locale (`fcode.i18n(key, args, { locale })`) resolves like the cloud's `by_locale`:
|
|
28
|
-
* key by key in the requested locale first, falling back to the merged translations
|
|
30
|
+
* key by key in the requested locale first, falling back to the merged translations — and the
|
|
31
|
+
* primary locale, which a call naming a bare language prefers when it broadens to a regional file
|
|
29
32
|
*/
|
|
30
33
|
export declare function resolveLocalTranslations(requestedLocale?: string, workspaceDir?: string): {
|
|
31
34
|
locale?: string;
|
|
35
|
+
primaryLocale?: string;
|
|
32
36
|
translations: Record<string, string>;
|
|
33
37
|
byLocale: Record<string, Record<string, string>>;
|
|
34
38
|
};
|