@anvil-works/anvil-cli 0.5.15 → 0.6.1-canary.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 +26 -0
- package/dist/SavePathRouter.d.ts.map +1 -1
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/cli.js +900 -233
- package/dist/componentBuiltinTypes.d.ts +11 -0
- package/dist/componentBuiltinTypes.d.ts.map +1 -0
- package/dist/index.js +906 -242
- package/dist/program.d.ts.map +1 -1
- package/dist/serviceSources.d.ts +5 -0
- package/dist/serviceSources.d.ts.map +1 -0
- package/dist/services/git-auth.d.ts +9 -0
- package/dist/services/git-auth.d.ts.map +1 -1
- package/dist/validators.d.ts +43 -3
- package/dist/validators.d.ts.map +1 -1
- package/dist/watch/SyncManager.d.ts +1 -0
- package/dist/watch/SyncManager.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33721,6 +33721,7 @@ var __webpack_exports__ = {};
|
|
|
33721
33721
|
var commander = __webpack_require__("./node_modules/.pnpm/commander@14.0.2/node_modules/commander/index.js");
|
|
33722
33722
|
const { program: esm_program, createCommand, createArgument, createOption, CommanderError, InvalidArgumentError, InvalidOptionArgumentError, Command, Argument, Option, Help } = commander;
|
|
33723
33723
|
var external_fs_ = __webpack_require__("fs");
|
|
33724
|
+
var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_);
|
|
33724
33725
|
const external_path_namespaceObject = require("path");
|
|
33725
33726
|
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
33726
33727
|
var semver = __webpack_require__("./node_modules/.pnpm/semver@7.7.3/node_modules/semver/index.js");
|
|
@@ -41840,18 +41841,27 @@ var __webpack_exports__ = {};
|
|
|
41840
41841
|
if (list.length > 1) return `(${list.join(` ${separator} `)})`;
|
|
41841
41842
|
return list[0] ?? "never";
|
|
41842
41843
|
}
|
|
41843
|
-
function
|
|
41844
|
+
function getDotPath(issue) {
|
|
41845
|
+
if (issue.path) {
|
|
41846
|
+
let key = "";
|
|
41847
|
+
for (const item of issue.path)if ("string" != typeof item.key && "number" != typeof item.key) return null;
|
|
41848
|
+
else if (key) key += `.${item.key}`;
|
|
41849
|
+
else key += item.key;
|
|
41850
|
+
return key;
|
|
41851
|
+
}
|
|
41852
|
+
return null;
|
|
41853
|
+
}
|
|
41854
|
+
function dist_nonEmpty(message$1) {
|
|
41844
41855
|
return {
|
|
41845
41856
|
kind: "validation",
|
|
41846
|
-
type: "
|
|
41847
|
-
reference:
|
|
41857
|
+
type: "non_empty",
|
|
41858
|
+
reference: dist_nonEmpty,
|
|
41848
41859
|
async: false,
|
|
41849
|
-
expects:
|
|
41850
|
-
requirement,
|
|
41860
|
+
expects: "!0",
|
|
41851
41861
|
message: message$1,
|
|
41852
41862
|
"~run" (dataset, config$1) {
|
|
41853
|
-
if (dataset.typed &&
|
|
41854
|
-
received:
|
|
41863
|
+
if (dataset.typed && 0 === dataset.value.length) _addIssue(this, "length", dataset, config$1, {
|
|
41864
|
+
received: "0"
|
|
41855
41865
|
});
|
|
41856
41866
|
return dataset;
|
|
41857
41867
|
}
|
|
@@ -41961,53 +41971,30 @@ var __webpack_exports__ = {};
|
|
|
41961
41971
|
}
|
|
41962
41972
|
};
|
|
41963
41973
|
}
|
|
41964
|
-
function
|
|
41965
|
-
return {
|
|
41966
|
-
kind: "schema",
|
|
41967
|
-
type: "nullable",
|
|
41968
|
-
reference: nullable,
|
|
41969
|
-
expects: `(${wrapped.expects} | null)`,
|
|
41970
|
-
async: false,
|
|
41971
|
-
wrapped,
|
|
41972
|
-
default: default_,
|
|
41973
|
-
get "~standard" () {
|
|
41974
|
-
return /* @__PURE__ */ _getStandardProps(this);
|
|
41975
|
-
},
|
|
41976
|
-
"~run" (dataset, config$1) {
|
|
41977
|
-
if (null === dataset.value) {
|
|
41978
|
-
if (void 0 !== this.default) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
|
|
41979
|
-
if (null === dataset.value) {
|
|
41980
|
-
dataset.typed = true;
|
|
41981
|
-
return dataset;
|
|
41982
|
-
}
|
|
41983
|
-
}
|
|
41984
|
-
return this.wrapped["~run"](dataset, config$1);
|
|
41985
|
-
}
|
|
41986
|
-
};
|
|
41987
|
-
}
|
|
41988
|
-
function dist_number(message$1) {
|
|
41974
|
+
function custom(check$1, message$1) {
|
|
41989
41975
|
return {
|
|
41990
41976
|
kind: "schema",
|
|
41991
|
-
type: "
|
|
41992
|
-
reference:
|
|
41993
|
-
expects: "
|
|
41977
|
+
type: "custom",
|
|
41978
|
+
reference: custom,
|
|
41979
|
+
expects: "unknown",
|
|
41994
41980
|
async: false,
|
|
41981
|
+
check: check$1,
|
|
41995
41982
|
message: message$1,
|
|
41996
41983
|
get "~standard" () {
|
|
41997
41984
|
return /* @__PURE__ */ _getStandardProps(this);
|
|
41998
41985
|
},
|
|
41999
41986
|
"~run" (dataset, config$1) {
|
|
42000
|
-
if (
|
|
42001
|
-
else dataset
|
|
41987
|
+
if (this.check(dataset.value)) dataset.typed = true;
|
|
41988
|
+
else _addIssue(this, "type", dataset, config$1);
|
|
42002
41989
|
return dataset;
|
|
42003
41990
|
}
|
|
42004
41991
|
};
|
|
42005
41992
|
}
|
|
42006
|
-
function
|
|
41993
|
+
function looseObject(entries$1, message$1) {
|
|
42007
41994
|
return {
|
|
42008
41995
|
kind: "schema",
|
|
42009
|
-
type: "
|
|
42010
|
-
reference:
|
|
41996
|
+
type: "loose_object",
|
|
41997
|
+
reference: looseObject,
|
|
42011
41998
|
expects: "Object",
|
|
42012
41999
|
async: false,
|
|
42013
42000
|
entries: entries$1,
|
|
@@ -42068,17 +42055,20 @@ var __webpack_exports__ = {};
|
|
|
42068
42055
|
if (config$1.abortEarly) break;
|
|
42069
42056
|
}
|
|
42070
42057
|
}
|
|
42058
|
+
if (!dataset.issues || !config$1.abortEarly) {
|
|
42059
|
+
for(const key in input)if (/* @__PURE__ */ _isValidObjectKey(input, key) && !(key in this.entries)) dataset.value[key] = input[key];
|
|
42060
|
+
}
|
|
42071
42061
|
} else _addIssue(this, "type", dataset, config$1);
|
|
42072
42062
|
return dataset;
|
|
42073
42063
|
}
|
|
42074
42064
|
};
|
|
42075
42065
|
}
|
|
42076
|
-
function
|
|
42066
|
+
function nullable(wrapped, default_) {
|
|
42077
42067
|
return {
|
|
42078
42068
|
kind: "schema",
|
|
42079
|
-
type: "
|
|
42080
|
-
reference:
|
|
42081
|
-
expects: `(${wrapped.expects} |
|
|
42069
|
+
type: "nullable",
|
|
42070
|
+
reference: nullable,
|
|
42071
|
+
expects: `(${wrapped.expects} | null)`,
|
|
42082
42072
|
async: false,
|
|
42083
42073
|
wrapped,
|
|
42084
42074
|
default: default_,
|
|
@@ -42086,9 +42076,9 @@ var __webpack_exports__ = {};
|
|
|
42086
42076
|
return /* @__PURE__ */ _getStandardProps(this);
|
|
42087
42077
|
},
|
|
42088
42078
|
"~run" (dataset, config$1) {
|
|
42089
|
-
if (
|
|
42079
|
+
if (null === dataset.value) {
|
|
42090
42080
|
if (void 0 !== this.default) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
|
|
42091
|
-
if (
|
|
42081
|
+
if (null === dataset.value) {
|
|
42092
42082
|
dataset.typed = true;
|
|
42093
42083
|
return dataset;
|
|
42094
42084
|
}
|
|
@@ -42097,151 +42087,81 @@ var __webpack_exports__ = {};
|
|
|
42097
42087
|
}
|
|
42098
42088
|
};
|
|
42099
42089
|
}
|
|
42100
|
-
function
|
|
42090
|
+
function dist_number(message$1) {
|
|
42101
42091
|
return {
|
|
42102
42092
|
kind: "schema",
|
|
42103
|
-
type: "
|
|
42104
|
-
reference:
|
|
42105
|
-
expects: "
|
|
42093
|
+
type: "number",
|
|
42094
|
+
reference: dist_number,
|
|
42095
|
+
expects: "number",
|
|
42106
42096
|
async: false,
|
|
42107
|
-
key,
|
|
42108
|
-
value: value$1,
|
|
42109
42097
|
message: message$1,
|
|
42110
42098
|
get "~standard" () {
|
|
42111
42099
|
return /* @__PURE__ */ _getStandardProps(this);
|
|
42112
42100
|
},
|
|
42113
42101
|
"~run" (dataset, config$1) {
|
|
42114
|
-
|
|
42115
|
-
|
|
42116
|
-
dataset.typed = true;
|
|
42117
|
-
dataset.value = {};
|
|
42118
|
-
for(const entryKey in input)if (/* @__PURE__ */ _isValidObjectKey(input, entryKey)) {
|
|
42119
|
-
const entryValue = input[entryKey];
|
|
42120
|
-
const keyDataset = this.key["~run"]({
|
|
42121
|
-
value: entryKey
|
|
42122
|
-
}, config$1);
|
|
42123
|
-
if (keyDataset.issues) {
|
|
42124
|
-
const pathItem = {
|
|
42125
|
-
type: "object",
|
|
42126
|
-
origin: "key",
|
|
42127
|
-
input,
|
|
42128
|
-
key: entryKey,
|
|
42129
|
-
value: entryValue
|
|
42130
|
-
};
|
|
42131
|
-
for (const issue of keyDataset.issues){
|
|
42132
|
-
issue.path = [
|
|
42133
|
-
pathItem
|
|
42134
|
-
];
|
|
42135
|
-
dataset.issues?.push(issue);
|
|
42136
|
-
}
|
|
42137
|
-
if (!dataset.issues) dataset.issues = keyDataset.issues;
|
|
42138
|
-
if (config$1.abortEarly) {
|
|
42139
|
-
dataset.typed = false;
|
|
42140
|
-
break;
|
|
42141
|
-
}
|
|
42142
|
-
}
|
|
42143
|
-
const valueDataset = this.value["~run"]({
|
|
42144
|
-
value: entryValue
|
|
42145
|
-
}, config$1);
|
|
42146
|
-
if (valueDataset.issues) {
|
|
42147
|
-
const pathItem = {
|
|
42148
|
-
type: "object",
|
|
42149
|
-
origin: "value",
|
|
42150
|
-
input,
|
|
42151
|
-
key: entryKey,
|
|
42152
|
-
value: entryValue
|
|
42153
|
-
};
|
|
42154
|
-
for (const issue of valueDataset.issues){
|
|
42155
|
-
if (issue.path) issue.path.unshift(pathItem);
|
|
42156
|
-
else issue.path = [
|
|
42157
|
-
pathItem
|
|
42158
|
-
];
|
|
42159
|
-
dataset.issues?.push(issue);
|
|
42160
|
-
}
|
|
42161
|
-
if (!dataset.issues) dataset.issues = valueDataset.issues;
|
|
42162
|
-
if (config$1.abortEarly) {
|
|
42163
|
-
dataset.typed = false;
|
|
42164
|
-
break;
|
|
42165
|
-
}
|
|
42166
|
-
}
|
|
42167
|
-
if (!keyDataset.typed || !valueDataset.typed) dataset.typed = false;
|
|
42168
|
-
if (keyDataset.typed) dataset.value[keyDataset.value] = valueDataset.value;
|
|
42169
|
-
}
|
|
42170
|
-
} else _addIssue(this, "type", dataset, config$1);
|
|
42102
|
+
if ("number" != typeof dataset.value || isNaN(dataset.value)) _addIssue(this, "type", dataset, config$1);
|
|
42103
|
+
else dataset.typed = true;
|
|
42171
42104
|
return dataset;
|
|
42172
42105
|
}
|
|
42173
42106
|
};
|
|
42174
42107
|
}
|
|
42175
|
-
function
|
|
42108
|
+
function optional(wrapped, default_) {
|
|
42176
42109
|
return {
|
|
42177
42110
|
kind: "schema",
|
|
42178
|
-
type: "
|
|
42179
|
-
reference:
|
|
42180
|
-
expects:
|
|
42111
|
+
type: "optional",
|
|
42112
|
+
reference: optional,
|
|
42113
|
+
expects: `(${wrapped.expects} | undefined)`,
|
|
42114
|
+
async: false,
|
|
42115
|
+
wrapped,
|
|
42116
|
+
default: default_,
|
|
42117
|
+
get "~standard" () {
|
|
42118
|
+
return /* @__PURE__ */ _getStandardProps(this);
|
|
42119
|
+
},
|
|
42120
|
+
"~run" (dataset, config$1) {
|
|
42121
|
+
if (void 0 === dataset.value) {
|
|
42122
|
+
if (void 0 !== this.default) dataset.value = /* @__PURE__ */ getDefault(this, dataset, config$1);
|
|
42123
|
+
if (void 0 === dataset.value) {
|
|
42124
|
+
dataset.typed = true;
|
|
42125
|
+
return dataset;
|
|
42126
|
+
}
|
|
42127
|
+
}
|
|
42128
|
+
return this.wrapped["~run"](dataset, config$1);
|
|
42129
|
+
}
|
|
42130
|
+
};
|
|
42131
|
+
}
|
|
42132
|
+
function picklist(options, message$1) {
|
|
42133
|
+
return {
|
|
42134
|
+
kind: "schema",
|
|
42135
|
+
type: "picklist",
|
|
42136
|
+
reference: picklist,
|
|
42137
|
+
expects: /* @__PURE__ */ _joinExpects(options.map(_stringify), "|"),
|
|
42181
42138
|
async: false,
|
|
42139
|
+
options,
|
|
42182
42140
|
message: message$1,
|
|
42183
42141
|
get "~standard" () {
|
|
42184
42142
|
return /* @__PURE__ */ _getStandardProps(this);
|
|
42185
42143
|
},
|
|
42186
42144
|
"~run" (dataset, config$1) {
|
|
42187
|
-
if (
|
|
42145
|
+
if (this.options.includes(dataset.value)) dataset.typed = true;
|
|
42188
42146
|
else _addIssue(this, "type", dataset, config$1);
|
|
42189
42147
|
return dataset;
|
|
42190
42148
|
}
|
|
42191
42149
|
};
|
|
42192
42150
|
}
|
|
42193
|
-
function
|
|
42194
|
-
let issues;
|
|
42195
|
-
if (datasets) for (const dataset of datasets)if (issues) issues.push(...dataset.issues);
|
|
42196
|
-
else issues = dataset.issues;
|
|
42197
|
-
return issues;
|
|
42198
|
-
}
|
|
42199
|
-
function union(options, message$1) {
|
|
42151
|
+
function dist_string(message$1) {
|
|
42200
42152
|
return {
|
|
42201
42153
|
kind: "schema",
|
|
42202
|
-
type: "
|
|
42203
|
-
reference:
|
|
42204
|
-
expects:
|
|
42154
|
+
type: "string",
|
|
42155
|
+
reference: dist_string,
|
|
42156
|
+
expects: "string",
|
|
42205
42157
|
async: false,
|
|
42206
|
-
options,
|
|
42207
42158
|
message: message$1,
|
|
42208
42159
|
get "~standard" () {
|
|
42209
42160
|
return /* @__PURE__ */ _getStandardProps(this);
|
|
42210
42161
|
},
|
|
42211
42162
|
"~run" (dataset, config$1) {
|
|
42212
|
-
|
|
42213
|
-
|
|
42214
|
-
let untypedDatasets;
|
|
42215
|
-
for (const schema of this.options){
|
|
42216
|
-
const optionDataset = schema["~run"]({
|
|
42217
|
-
value: dataset.value
|
|
42218
|
-
}, config$1);
|
|
42219
|
-
if (optionDataset.typed) if (optionDataset.issues) if (typedDatasets) typedDatasets.push(optionDataset);
|
|
42220
|
-
else typedDatasets = [
|
|
42221
|
-
optionDataset
|
|
42222
|
-
];
|
|
42223
|
-
else {
|
|
42224
|
-
validDataset = optionDataset;
|
|
42225
|
-
break;
|
|
42226
|
-
}
|
|
42227
|
-
else if (untypedDatasets) untypedDatasets.push(optionDataset);
|
|
42228
|
-
else untypedDatasets = [
|
|
42229
|
-
optionDataset
|
|
42230
|
-
];
|
|
42231
|
-
}
|
|
42232
|
-
if (validDataset) return validDataset;
|
|
42233
|
-
if (typedDatasets) {
|
|
42234
|
-
if (1 === typedDatasets.length) return typedDatasets[0];
|
|
42235
|
-
_addIssue(this, "type", dataset, config$1, {
|
|
42236
|
-
issues: /* @__PURE__ */ _subIssues(typedDatasets)
|
|
42237
|
-
});
|
|
42238
|
-
dataset.typed = true;
|
|
42239
|
-
} else {
|
|
42240
|
-
if (untypedDatasets?.length === 1) return untypedDatasets[0];
|
|
42241
|
-
_addIssue(this, "type", dataset, config$1, {
|
|
42242
|
-
issues: /* @__PURE__ */ _subIssues(untypedDatasets)
|
|
42243
|
-
});
|
|
42244
|
-
}
|
|
42163
|
+
if ("string" == typeof dataset.value) dataset.typed = true;
|
|
42164
|
+
else _addIssue(this, "type", dataset, config$1);
|
|
42245
42165
|
return dataset;
|
|
42246
42166
|
}
|
|
42247
42167
|
};
|
|
@@ -42584,7 +42504,7 @@ var __webpack_exports__ = {};
|
|
|
42584
42504
|
if (!preferredEditors.includes(normalized)) throw new Error(`✖ Error: preferredEditor must be one of: ${preferredEditors.join(", ")} (alias: code -> vscode)`);
|
|
42585
42505
|
return normalized;
|
|
42586
42506
|
}
|
|
42587
|
-
if ("anvilUrl" === key) return value
|
|
42507
|
+
if ("anvilUrl" === key) return normalizeAnvilUrl(value);
|
|
42588
42508
|
return coerceConfigValue(key, value);
|
|
42589
42509
|
}
|
|
42590
42510
|
function getPreferredEditorCommand(preferredEditorValue) {
|
|
@@ -48029,91 +47949,753 @@ var __webpack_exports__ = {};
|
|
|
48029
47949
|
return filesToRemove;
|
|
48030
47950
|
}
|
|
48031
47951
|
var external_crypto_ = __webpack_require__("crypto");
|
|
48032
|
-
const
|
|
48033
|
-
|
|
48034
|
-
|
|
48035
|
-
|
|
48036
|
-
|
|
48037
|
-
|
|
48038
|
-
|
|
48039
|
-
|
|
48040
|
-
|
|
48041
|
-
|
|
48042
|
-
|
|
48043
|
-
|
|
48044
|
-
|
|
48045
|
-
|
|
48046
|
-
|
|
48047
|
-
|
|
48048
|
-
|
|
48049
|
-
|
|
48050
|
-
|
|
48051
|
-
|
|
48052
|
-
|
|
48053
|
-
|
|
48054
|
-
|
|
47952
|
+
const BUILTIN_COMPONENT_TYPES = new Set([
|
|
47953
|
+
"Label",
|
|
47954
|
+
"RichText",
|
|
47955
|
+
"Link",
|
|
47956
|
+
"Button",
|
|
47957
|
+
"TextBox",
|
|
47958
|
+
"TextArea",
|
|
47959
|
+
"CheckBox",
|
|
47960
|
+
"RadioButton",
|
|
47961
|
+
"DropDown",
|
|
47962
|
+
"DatePicker",
|
|
47963
|
+
"Timer",
|
|
47964
|
+
"Canvas",
|
|
47965
|
+
"Image",
|
|
47966
|
+
"YouTubeVideo",
|
|
47967
|
+
"GoogleMap",
|
|
47968
|
+
"Plot",
|
|
47969
|
+
"FileLoader",
|
|
47970
|
+
"Spacer",
|
|
47971
|
+
"GridPanel",
|
|
47972
|
+
"LinearPanel",
|
|
47973
|
+
"XYPanel",
|
|
47974
|
+
"ColumnPanel",
|
|
47975
|
+
"HtmlPanel",
|
|
47976
|
+
"RepeatingPanel",
|
|
47977
|
+
"DataGrid",
|
|
47978
|
+
"DataRowPanel",
|
|
47979
|
+
"CustomComponent",
|
|
47980
|
+
"LayoutSlot",
|
|
47981
|
+
"FlowPanel",
|
|
47982
|
+
"HtmlTemplate"
|
|
47983
|
+
]);
|
|
47984
|
+
const KNOWN_RUNTIME_SERVICE_SOURCES = new Set([
|
|
47985
|
+
"/runtime/services/tables.yml",
|
|
47986
|
+
"/runtime/services/google.yml",
|
|
47987
|
+
"/runtime/services/uplink.yml",
|
|
47988
|
+
"/runtime/services/stripe.yml",
|
|
47989
|
+
"/runtime/services/segment.yml",
|
|
47990
|
+
"/runtime/services/facebook.yml",
|
|
47991
|
+
"/runtime/services/anvil/users.yml",
|
|
47992
|
+
"/runtime/services/anvil/secrets.yml",
|
|
47993
|
+
"/runtime/services/anvil/saml.yml",
|
|
47994
|
+
"/runtime/services/anvil/microsoft.yml",
|
|
47995
|
+
"/runtime/services/anvil/email.yml",
|
|
47996
|
+
"/runtime/services/anvil/files.yml"
|
|
47997
|
+
]);
|
|
47998
|
+
const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
47999
|
+
const DEP_ID_RE = /^dep_[A-Za-z0-9]+$/;
|
|
48000
|
+
const DB_ACCESS_LEVELS = new Set([
|
|
48001
|
+
"none",
|
|
48002
|
+
"search",
|
|
48003
|
+
"full"
|
|
48004
|
+
]);
|
|
48005
|
+
const DB_BASIC_COLUMN_TYPES = new Set([
|
|
48006
|
+
"string",
|
|
48007
|
+
"number",
|
|
48008
|
+
"bool",
|
|
48009
|
+
"date",
|
|
48010
|
+
"datetime",
|
|
48011
|
+
"media",
|
|
48012
|
+
"simpleObject"
|
|
48013
|
+
]);
|
|
48014
|
+
const DB_LINK_COLUMN_TYPES = new Set([
|
|
48015
|
+
"link_single",
|
|
48016
|
+
"link_multiple"
|
|
48017
|
+
]);
|
|
48018
|
+
const DB_TABLE_INDEX_TYPES = new Set([
|
|
48019
|
+
"b_tree",
|
|
48020
|
+
"trigram",
|
|
48021
|
+
"full_text"
|
|
48022
|
+
]);
|
|
48023
|
+
const SCHEDULE_EVERY_VALUES = new Set([
|
|
48024
|
+
"minute",
|
|
48025
|
+
"hour",
|
|
48026
|
+
"day",
|
|
48027
|
+
"week",
|
|
48028
|
+
"month"
|
|
48029
|
+
]);
|
|
48030
|
+
const CLIENT_VERSIONS = new Set([
|
|
48031
|
+
"2",
|
|
48032
|
+
"3"
|
|
48033
|
+
]);
|
|
48034
|
+
const PYTHON_KEYWORDS = new Set([
|
|
48035
|
+
"False",
|
|
48036
|
+
"None",
|
|
48037
|
+
"True",
|
|
48038
|
+
"and",
|
|
48039
|
+
"as",
|
|
48040
|
+
"assert",
|
|
48041
|
+
"async",
|
|
48042
|
+
"await",
|
|
48043
|
+
"break",
|
|
48044
|
+
"class",
|
|
48045
|
+
"continue",
|
|
48046
|
+
"def",
|
|
48047
|
+
"del",
|
|
48048
|
+
"elif",
|
|
48049
|
+
"else",
|
|
48050
|
+
"except",
|
|
48051
|
+
"finally",
|
|
48052
|
+
"for",
|
|
48053
|
+
"from",
|
|
48054
|
+
"global",
|
|
48055
|
+
"if",
|
|
48056
|
+
"import",
|
|
48057
|
+
"in",
|
|
48058
|
+
"is",
|
|
48059
|
+
"lambda",
|
|
48060
|
+
"nonlocal",
|
|
48061
|
+
"not",
|
|
48062
|
+
"or",
|
|
48063
|
+
"pass",
|
|
48064
|
+
"raise",
|
|
48065
|
+
"return",
|
|
48066
|
+
"try",
|
|
48067
|
+
"while",
|
|
48068
|
+
"with",
|
|
48069
|
+
"yield",
|
|
48070
|
+
"match",
|
|
48071
|
+
"case"
|
|
48072
|
+
]);
|
|
48073
|
+
const PROPERTY_TYPES = new Set([
|
|
48074
|
+
"string",
|
|
48075
|
+
"number",
|
|
48076
|
+
"color",
|
|
48077
|
+
"boolean",
|
|
48078
|
+
"text[]",
|
|
48079
|
+
"enum",
|
|
48080
|
+
"icon",
|
|
48081
|
+
"form",
|
|
48082
|
+
"object",
|
|
48083
|
+
"uri",
|
|
48084
|
+
"margin",
|
|
48085
|
+
"padding",
|
|
48086
|
+
"spacing"
|
|
48087
|
+
]);
|
|
48088
|
+
function isValidFormReference(typeName) {
|
|
48089
|
+
if (!typeName.startsWith("form:")) return false;
|
|
48090
|
+
const rest = typeName.slice(5);
|
|
48091
|
+
if (0 === rest.length) return false;
|
|
48092
|
+
let classPath = rest;
|
|
48093
|
+
if (rest.startsWith("dep_")) {
|
|
48094
|
+
const splitIndex = rest.indexOf(":");
|
|
48095
|
+
if (splitIndex <= 0) return false;
|
|
48096
|
+
const depId = rest.slice(0, splitIndex);
|
|
48097
|
+
if (!DEP_ID_RE.test(depId)) return false;
|
|
48098
|
+
classPath = rest.slice(splitIndex + 1);
|
|
48099
|
+
}
|
|
48100
|
+
if (0 === classPath.length) return false;
|
|
48101
|
+
return classPath.split(".").every((part)=>IDENTIFIER_RE.test(part));
|
|
48102
|
+
}
|
|
48103
|
+
function isBuiltinComponentType(typeName) {
|
|
48104
|
+
return BUILTIN_COMPONENT_TYPES.has(typeName);
|
|
48105
|
+
}
|
|
48106
|
+
function validators_isPlainObject(value) {
|
|
48107
|
+
return "object" == typeof value && null !== value && !Array.isArray(value);
|
|
48108
|
+
}
|
|
48109
|
+
function pushIssue(issues, path, message) {
|
|
48110
|
+
issues.push({
|
|
48111
|
+
path,
|
|
48112
|
+
message
|
|
48113
|
+
});
|
|
48114
|
+
}
|
|
48115
|
+
function formatValidationPath(path) {
|
|
48116
|
+
return path || "root";
|
|
48117
|
+
}
|
|
48118
|
+
function validateIdentifier(value, path, issues) {
|
|
48119
|
+
if ("string" != typeof value || 0 === value.length) return void pushIssue(issues, path, "must be a non-empty string");
|
|
48120
|
+
if (!IDENTIFIER_RE.test(value)) return void pushIssue(issues, path, "must be a valid identifier");
|
|
48121
|
+
if (PYTHON_KEYWORDS.has(value)) pushIssue(issues, path, "must be a valid Python identifier (not a keyword)");
|
|
48122
|
+
}
|
|
48123
|
+
function validateComponentType(value, path, issues) {
|
|
48124
|
+
if ("string" != typeof value || 0 === value.length) return void pushIssue(issues, path, "must be a non-empty string");
|
|
48125
|
+
const typeName = value;
|
|
48126
|
+
if (typeName.startsWith("form:")) {
|
|
48127
|
+
if (!isValidFormReference(typeName)) pushIssue(issues, path, "must be a valid form: reference (form:Module.Form, form:dep_xxx:Package.Form)");
|
|
48128
|
+
return;
|
|
48129
|
+
}
|
|
48130
|
+
if (!IDENTIFIER_RE.test(typeName)) return void pushIssue(issues, path, "must be a valid identifier");
|
|
48131
|
+
if (PYTHON_KEYWORDS.has(typeName)) return void pushIssue(issues, path, "must be a valid Python identifier (not a keyword)");
|
|
48132
|
+
if (!isBuiltinComponentType(typeName)) pushIssue(issues, path, "must be a known Anvil component type (see componentIcons.ts) or a form: reference");
|
|
48133
|
+
}
|
|
48134
|
+
function validatePropertiesObject(value, path, issues) {
|
|
48135
|
+
if (void 0 !== value && !validators_isPlainObject(value)) pushIssue(issues, path, "must be an object");
|
|
48136
|
+
}
|
|
48137
|
+
function validateEventBindingsObject(value, path, issues) {
|
|
48138
|
+
if (void 0 === value) return;
|
|
48139
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48140
|
+
for (const [eventName, handlerName] of Object.entries(value)){
|
|
48141
|
+
validateIdentifier(eventName, `${path}.${eventName}.name`, issues);
|
|
48142
|
+
validateIdentifier(handlerName, `${path}.${eventName}`, issues);
|
|
48143
|
+
}
|
|
48144
|
+
}
|
|
48145
|
+
function validateDataBindings(value, path, issues) {
|
|
48146
|
+
if (void 0 === value) return;
|
|
48147
|
+
if (!Array.isArray(value)) return void pushIssue(issues, path, "must be an array");
|
|
48148
|
+
value.forEach((binding, index)=>{
|
|
48149
|
+
const bindingPath = `${path}[${index}]`;
|
|
48150
|
+
if (!validators_isPlainObject(binding)) return void pushIssue(issues, bindingPath, "must be an object");
|
|
48151
|
+
if ("string" != typeof binding.property || 0 === binding.property.length) pushIssue(issues, `${bindingPath}.property`, "must be a non-empty string");
|
|
48152
|
+
if ("string" != typeof binding.code || 0 === binding.code.length) pushIssue(issues, `${bindingPath}.code`, "must be a non-empty string");
|
|
48153
|
+
if (void 0 !== binding.writeback && "boolean" != typeof binding.writeback) pushIssue(issues, `${bindingPath}.writeback`, "must be a boolean");
|
|
48154
|
+
});
|
|
48155
|
+
}
|
|
48156
|
+
function validateComponentArray(components, pathPrefix, issues, seenNames) {
|
|
48157
|
+
components.forEach((component, index)=>{
|
|
48158
|
+
const childPath = `${pathPrefix}[${index}]`;
|
|
48159
|
+
validateComponentNode(component, childPath, issues, seenNames);
|
|
48160
|
+
if (validators_isPlainObject(component) && "string" == typeof component.name && component.name.length > 0) {
|
|
48161
|
+
if (seenNames.has(component.name)) pushIssue(issues, `${childPath}.name`, "must be unique across form components");
|
|
48162
|
+
seenNames.add(component.name);
|
|
48163
|
+
}
|
|
48164
|
+
});
|
|
48165
|
+
}
|
|
48166
|
+
function validateComponentNode(value, path, issues, seenNames) {
|
|
48167
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48168
|
+
validateComponentType(value.type, `${path}.type`, issues);
|
|
48169
|
+
if (void 0 === value.name || "string" != typeof value.name || 0 === value.name.length) pushIssue(issues, `${path}.name`, "must be a non-empty string");
|
|
48170
|
+
else validateIdentifier(value.name, `${path}.name`, issues);
|
|
48171
|
+
validatePropertiesObject(value.properties, `${path}.properties`, issues);
|
|
48172
|
+
validatePropertiesObject(value.layout_properties, `${path}.layout_properties`, issues);
|
|
48173
|
+
validateEventBindingsObject(value.event_bindings, `${path}.event_bindings`, issues);
|
|
48174
|
+
validateDataBindings(value.data_bindings, `${path}.data_bindings`, issues);
|
|
48175
|
+
if (void 0 !== value.components) if (Array.isArray(value.components)) validateComponentArray(value.components, `${path}.components`, issues, seenNames);
|
|
48176
|
+
else pushIssue(issues, `${path}.components`, "must be an array");
|
|
48177
|
+
}
|
|
48178
|
+
function validatePropertyDefinition(value, path, issues) {
|
|
48179
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48180
|
+
validateIdentifier(value.name, `${path}.name`, issues);
|
|
48181
|
+
if ("string" != typeof value.type || !PROPERTY_TYPES.has(value.type)) pushIssue(issues, `${path}.type`, "must be a supported property type");
|
|
48182
|
+
if (void 0 !== value.description && "string" != typeof value.description) pushIssue(issues, `${path}.description`, "must be a string");
|
|
48183
|
+
if (void 0 !== value.default_binding_prop && "boolean" != typeof value.default_binding_prop) pushIssue(issues, `${path}.default_binding_prop`, "must be a boolean");
|
|
48184
|
+
if (void 0 !== value.allow_binding_writeback && "boolean" != typeof value.allow_binding_writeback) pushIssue(issues, `${path}.allow_binding_writeback`, "must be a boolean");
|
|
48185
|
+
if (void 0 !== value.binding_writeback_events) if (Array.isArray(value.binding_writeback_events)) value.binding_writeback_events.forEach((eventName, index)=>{
|
|
48186
|
+
if ("string" != typeof eventName || 0 === eventName.length) pushIssue(issues, `${path}.binding_writeback_events[${index}]`, "must be a non-empty string");
|
|
48187
|
+
});
|
|
48188
|
+
else pushIssue(issues, `${path}.binding_writeback_events`, "must be an array");
|
|
48189
|
+
if ("enum" === value.type) {
|
|
48190
|
+
if (!Array.isArray(value.options) || value.options.some((option)=>"string" != typeof option)) pushIssue(issues, `${path}.options`, "must be an array of strings for enum properties");
|
|
48191
|
+
}
|
|
48192
|
+
}
|
|
48193
|
+
function validateEventDefinition(value, path, issues) {
|
|
48194
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48195
|
+
validateIdentifier(value.name, `${path}.name`, issues);
|
|
48196
|
+
if (void 0 !== value.description && "string" != typeof value.description) pushIssue(issues, `${path}.description`, "must be a string");
|
|
48197
|
+
if (void 0 !== value.default_event && "boolean" != typeof value.default_event) pushIssue(issues, `${path}.default_event`, "must be a boolean");
|
|
48198
|
+
if (void 0 !== value.parameters) if (Array.isArray(value.parameters)) value.parameters.forEach((parameter, index)=>{
|
|
48199
|
+
if (!validators_isPlainObject(parameter)) return void pushIssue(issues, `${path}.parameters[${index}]`, "must be an object");
|
|
48200
|
+
validateIdentifier(parameter.name, `${path}.parameters[${index}].name`, issues);
|
|
48201
|
+
if (void 0 !== parameter.description && "string" != typeof parameter.description) pushIssue(issues, `${path}.parameters[${index}].description`, "must be a string");
|
|
48202
|
+
});
|
|
48203
|
+
else pushIssue(issues, `${path}.parameters`, "must be an array");
|
|
48204
|
+
}
|
|
48205
|
+
function validateSlotDefinition(value, path, issues, seenNames) {
|
|
48206
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48207
|
+
if ("number" != typeof value.index || !Number.isInteger(value.index) || value.index < 0) pushIssue(issues, `${path}.index`, "must be a non-negative integer");
|
|
48208
|
+
validatePropertiesObject(value.set_layout_properties, `${path}.set_layout_properties`, issues);
|
|
48209
|
+
if (validators_isPlainObject(value.target)) {
|
|
48210
|
+
if ("container" !== value.target.type && "slot" !== value.target.type) pushIssue(issues, `${path}.target.type`, "must be 'container' or 'slot'");
|
|
48211
|
+
validateIdentifier(value.target.name, `${path}.target.name`, issues);
|
|
48212
|
+
} else pushIssue(issues, `${path}.target`, "must be an object");
|
|
48213
|
+
if (void 0 !== value.template) validateComponentNode(value.template, `${path}.template`, issues, seenNames);
|
|
48214
|
+
}
|
|
48215
|
+
function validateFormTemplateData(value) {
|
|
48216
|
+
const issues = [];
|
|
48217
|
+
const componentNames = new Set();
|
|
48218
|
+
if (!validators_isPlainObject(value)) {
|
|
48219
|
+
pushIssue(issues, "", "must be a YAML object");
|
|
48220
|
+
return issues;
|
|
48221
|
+
}
|
|
48222
|
+
const hasContainer = void 0 !== value.container;
|
|
48223
|
+
const hasLayout = void 0 !== value.layout;
|
|
48224
|
+
const hasComponents = void 0 !== value.components;
|
|
48225
|
+
const hasComponentsBySlot = void 0 !== value.components_by_slot;
|
|
48226
|
+
if (hasContainer && hasLayout) pushIssue(issues, "", "cannot define both container and layout");
|
|
48227
|
+
if (!hasContainer && !hasLayout) pushIssue(issues, "", "must define either container or layout");
|
|
48228
|
+
if (void 0 !== value.is_package && "boolean" != typeof value.is_package) pushIssue(issues, "is_package", "must be a boolean");
|
|
48229
|
+
if (hasContainer) if (validators_isPlainObject(value.container)) {
|
|
48230
|
+
validateComponentType(value.container.type, "container.type", issues);
|
|
48231
|
+
validatePropertiesObject(value.container.properties, "container.properties", issues);
|
|
48232
|
+
validatePropertiesObject(value.container.layout_properties, "container.layout_properties", issues);
|
|
48233
|
+
validateEventBindingsObject(value.container.event_bindings, "container.event_bindings", issues);
|
|
48234
|
+
validateDataBindings(value.container.data_bindings, "container.data_bindings", issues);
|
|
48235
|
+
} else pushIssue(issues, "container", "must be an object");
|
|
48236
|
+
if (hasLayout) if (validators_isPlainObject(value.layout)) {
|
|
48237
|
+
validateComponentType(value.layout.type, "layout.type", issues);
|
|
48238
|
+
validatePropertiesObject(value.layout.properties, "layout.properties", issues);
|
|
48239
|
+
validateEventBindingsObject(value.layout.event_bindings, "layout.event_bindings", issues);
|
|
48240
|
+
validateEventBindingsObject(value.layout.form_event_bindings, "layout.form_event_bindings", issues);
|
|
48241
|
+
validateDataBindings(value.layout.data_bindings, "layout.data_bindings", issues);
|
|
48242
|
+
} else pushIssue(issues, "layout", "must be an object");
|
|
48243
|
+
if (hasComponents && hasLayout) pushIssue(issues, "components", "can only be used with container-based forms");
|
|
48244
|
+
else if (hasComponents && !hasContainer) pushIssue(issues, "components", "can only be used with container-based forms");
|
|
48245
|
+
else if (hasComponents) if (Array.isArray(value.components)) validateComponentArray(value.components, "components", issues, componentNames);
|
|
48246
|
+
else pushIssue(issues, "components", "must be an array");
|
|
48247
|
+
if (hasComponentsBySlot && hasContainer) pushIssue(issues, "components_by_slot", "can only be used with layout-based forms");
|
|
48248
|
+
else if (hasComponentsBySlot && !hasLayout) pushIssue(issues, "components_by_slot", "can only be used with layout-based forms");
|
|
48249
|
+
else if (hasComponentsBySlot) if (validators_isPlainObject(value.components_by_slot)) for (const [slotName, slotComponents] of Object.entries(value.components_by_slot)){
|
|
48250
|
+
if (!Array.isArray(slotComponents)) {
|
|
48251
|
+
pushIssue(issues, `components_by_slot.${slotName}`, "must be an array");
|
|
48252
|
+
continue;
|
|
48253
|
+
}
|
|
48254
|
+
validateComponentArray(slotComponents, `components_by_slot.${slotName}`, issues, componentNames);
|
|
48255
|
+
}
|
|
48256
|
+
else pushIssue(issues, "components_by_slot", "must be an object");
|
|
48257
|
+
if (void 0 !== value.custom_component && "boolean" != typeof value.custom_component) pushIssue(issues, "custom_component", "must be a boolean");
|
|
48258
|
+
if (void 0 !== value.custom_component_container && "boolean" != typeof value.custom_component_container) pushIssue(issues, "custom_component_container", "must be a boolean");
|
|
48259
|
+
if (true === value.custom_component_container && true !== value.custom_component) pushIssue(issues, "custom_component_container", "requires custom_component: true");
|
|
48260
|
+
const properties = value.properties;
|
|
48261
|
+
if (void 0 !== properties) if (Array.isArray(properties)) {
|
|
48262
|
+
const seenNames = new Set();
|
|
48263
|
+
let defaultBindingPropCount = 0;
|
|
48264
|
+
properties.forEach((property, index)=>{
|
|
48265
|
+
validatePropertyDefinition(property, `properties[${index}]`, issues);
|
|
48266
|
+
if (!validators_isPlainObject(property) || "string" != typeof property.name) return;
|
|
48267
|
+
if (seenNames.has(property.name)) pushIssue(issues, `properties[${index}].name`, "must be unique");
|
|
48268
|
+
seenNames.add(property.name);
|
|
48269
|
+
if (property.default_binding_prop) defaultBindingPropCount++;
|
|
48270
|
+
});
|
|
48271
|
+
if (defaultBindingPropCount > 1) pushIssue(issues, "properties", "can only define one default_binding_prop");
|
|
48272
|
+
} else pushIssue(issues, "properties", "must be an array");
|
|
48273
|
+
const events = value.events;
|
|
48274
|
+
const eventNames = new Set();
|
|
48275
|
+
if (void 0 !== events) if (Array.isArray(events)) {
|
|
48276
|
+
let defaultEventCount = 0;
|
|
48277
|
+
events.forEach((event, index)=>{
|
|
48278
|
+
validateEventDefinition(event, `events[${index}]`, issues);
|
|
48279
|
+
if (!validators_isPlainObject(event) || "string" != typeof event.name) return;
|
|
48280
|
+
if (eventNames.has(event.name)) pushIssue(issues, `events[${index}].name`, "must be unique");
|
|
48281
|
+
eventNames.add(event.name);
|
|
48282
|
+
if (event.default_event) defaultEventCount++;
|
|
48283
|
+
});
|
|
48284
|
+
if (defaultEventCount > 1) pushIssue(issues, "events", "can only define one default_event");
|
|
48285
|
+
} else pushIssue(issues, "events", "must be an array");
|
|
48286
|
+
if (Array.isArray(properties)) properties.forEach((property, index)=>{
|
|
48287
|
+
if (!validators_isPlainObject(property) || !Array.isArray(property.binding_writeback_events)) return;
|
|
48288
|
+
property.binding_writeback_events.forEach((eventName, eventIndex)=>{
|
|
48289
|
+
if ("string" == typeof eventName && !eventNames.has(eventName)) pushIssue(issues, `properties[${index}].binding_writeback_events[${eventIndex}]`, "must reference a defined event");
|
|
48290
|
+
});
|
|
48291
|
+
});
|
|
48292
|
+
if (void 0 !== value.slots) if (validators_isPlainObject(value.slots)) for (const [slotName, slotDef] of Object.entries(value.slots)){
|
|
48293
|
+
validateIdentifier(slotName, `slots.${slotName}.name`, issues);
|
|
48294
|
+
validateSlotDefinition(slotDef, `slots.${slotName}`, issues, componentNames);
|
|
48295
|
+
}
|
|
48296
|
+
else pushIssue(issues, "slots", "must be an object");
|
|
48297
|
+
if (void 0 !== value.item_type) if (validators_isPlainObject(value.item_type)) {
|
|
48298
|
+
if ("number" != typeof value.item_type.table_id) pushIssue(issues, "item_type.table_id", "must be a number");
|
|
48299
|
+
} else pushIssue(issues, "item_type", "must be an object");
|
|
48300
|
+
return issues;
|
|
48301
|
+
}
|
|
48055
48302
|
function validateFormTemplate(yamlContent) {
|
|
48056
48303
|
try {
|
|
48057
48304
|
const data = js_yaml_load(yamlContent);
|
|
48058
|
-
const
|
|
48059
|
-
if (
|
|
48060
|
-
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
}
|
|
48064
|
-
return
|
|
48305
|
+
const issues = validateFormTemplateData(data);
|
|
48306
|
+
if (issues.length > 0) return {
|
|
48307
|
+
ok: false,
|
|
48308
|
+
message: "form_template.yaml is invalid for Anvil",
|
|
48309
|
+
issues
|
|
48310
|
+
};
|
|
48311
|
+
return {
|
|
48312
|
+
ok: true
|
|
48313
|
+
};
|
|
48065
48314
|
} catch (error) {
|
|
48066
|
-
|
|
48067
|
-
|
|
48315
|
+
return {
|
|
48316
|
+
ok: false,
|
|
48317
|
+
message: "form_template.yaml is not valid YAML",
|
|
48318
|
+
issues: [
|
|
48319
|
+
{
|
|
48320
|
+
path: "",
|
|
48321
|
+
message: error instanceof Error ? error.message : String(error)
|
|
48322
|
+
}
|
|
48323
|
+
]
|
|
48324
|
+
};
|
|
48325
|
+
}
|
|
48326
|
+
}
|
|
48327
|
+
const serviceConfigObject = custom((input)=>validators_isPlainObject(input), "must be an object");
|
|
48328
|
+
const AnvilServiceEntrySchema = looseObject({
|
|
48329
|
+
source: pipe(dist_string(), dist_nonEmpty()),
|
|
48330
|
+
client_config: optional(serviceConfigObject),
|
|
48331
|
+
server_config: optional(serviceConfigObject)
|
|
48332
|
+
});
|
|
48333
|
+
const RuntimeOptionsSchema = looseObject({
|
|
48334
|
+
version: dist_number(),
|
|
48335
|
+
client_version: optional(picklist([
|
|
48336
|
+
"2",
|
|
48337
|
+
"3"
|
|
48338
|
+
])),
|
|
48339
|
+
preview_v3: optional(dist_boolean()),
|
|
48340
|
+
server_version: optional(dist_string()),
|
|
48341
|
+
server_persist: optional(dist_boolean()),
|
|
48342
|
+
legacy_features: optional(looseObject({
|
|
48343
|
+
class_names: optional(dist_boolean()),
|
|
48344
|
+
bootstrap3: optional(dist_boolean()),
|
|
48345
|
+
__dict__: optional(dist_boolean()),
|
|
48346
|
+
root_container: optional(dist_boolean())
|
|
48347
|
+
})),
|
|
48348
|
+
server_spec: optional(nullable(looseObject({
|
|
48349
|
+
base: optional(dist_string())
|
|
48350
|
+
})))
|
|
48351
|
+
});
|
|
48352
|
+
function validateRuntimeOptions(value, path) {
|
|
48353
|
+
const issues = [];
|
|
48354
|
+
if (void 0 === value) return issues;
|
|
48355
|
+
if (!validators_isPlainObject(value)) return issues;
|
|
48356
|
+
if (void 0 !== value.client_version) {
|
|
48357
|
+
if ("string" != typeof value.client_version || !CLIENT_VERSIONS.has(value.client_version)) pushIssue(issues, `${path}.client_version`, 'must be "2" or "3"');
|
|
48358
|
+
}
|
|
48359
|
+
if (void 0 !== value.server_spec && null !== value.server_spec) {
|
|
48360
|
+
if (!validators_isPlainObject(value.server_spec)) return issues;
|
|
48361
|
+
if ("requirements" in value.server_spec) pushIssue(issues, `${path}.server_spec.requirements`, "is not a valid anvil.yaml field; requirements come from server_code/requirements.txt");
|
|
48362
|
+
if (void 0 !== value.server_spec.base && "string" != typeof value.server_spec.base) pushIssue(issues, `${path}.server_spec.base`, "must be a string");
|
|
48363
|
+
}
|
|
48364
|
+
return issues;
|
|
48365
|
+
}
|
|
48366
|
+
function validateDbSchema(value, path) {
|
|
48367
|
+
const issues = [];
|
|
48368
|
+
if (void 0 === value) return issues;
|
|
48369
|
+
if (null === value) {
|
|
48370
|
+
pushIssue(issues, path, "must be an object, an empty array, or omitted");
|
|
48371
|
+
return issues;
|
|
48372
|
+
}
|
|
48373
|
+
if (Array.isArray(value)) {
|
|
48374
|
+
if (0 === value.length) return issues;
|
|
48375
|
+
pushIssue(issues, path, "must be an object mapping table names to schemas, or an empty array");
|
|
48376
|
+
return issues;
|
|
48377
|
+
}
|
|
48378
|
+
if (!validators_isPlainObject(value)) {
|
|
48379
|
+
pushIssue(issues, path, "must be an object mapping table names to schemas");
|
|
48380
|
+
return issues;
|
|
48381
|
+
}
|
|
48382
|
+
for (const tableName of Object.keys(value)){
|
|
48383
|
+
const tablePath = `${path}.${tableName}`;
|
|
48384
|
+
if (!IDENTIFIER_RE.test(tableName)) {
|
|
48385
|
+
pushIssue(issues, tablePath, "must be a valid Python identifier");
|
|
48386
|
+
continue;
|
|
48387
|
+
}
|
|
48388
|
+
const table = value[tableName];
|
|
48389
|
+
if (!validators_isPlainObject(table)) {
|
|
48390
|
+
pushIssue(issues, tablePath, "must be an object");
|
|
48391
|
+
continue;
|
|
48392
|
+
}
|
|
48393
|
+
for (const req of [
|
|
48394
|
+
"client",
|
|
48395
|
+
"server",
|
|
48396
|
+
"title",
|
|
48397
|
+
"columns"
|
|
48398
|
+
])if (void 0 === table[req]) pushIssue(issues, `${tablePath}.${req}`, "is required");
|
|
48399
|
+
if (void 0 !== table.client) {
|
|
48400
|
+
if ("string" != typeof table.client || !DB_ACCESS_LEVELS.has(table.client)) pushIssue(issues, `${tablePath}.client`, 'must be "none", "search", or "full"');
|
|
48401
|
+
}
|
|
48402
|
+
if (void 0 !== table.server) {
|
|
48403
|
+
if ("string" != typeof table.server || !DB_ACCESS_LEVELS.has(table.server)) pushIssue(issues, `${tablePath}.server`, 'must be "none", "search", or "full"');
|
|
48404
|
+
}
|
|
48405
|
+
if (void 0 !== table.title) {
|
|
48406
|
+
if ("string" != typeof table.title || 0 === table.title.length) pushIssue(issues, `${tablePath}.title`, "must be a non-empty string");
|
|
48407
|
+
}
|
|
48408
|
+
if (void 0 !== table.columns) if (Array.isArray(table.columns)) table.columns.forEach((column, index)=>{
|
|
48409
|
+
validateDbSchemaColumn(column, `${tablePath}.columns[${index}]`, issues);
|
|
48410
|
+
});
|
|
48411
|
+
else pushIssue(issues, `${tablePath}.columns`, "must be an array");
|
|
48412
|
+
if (void 0 !== table.indexes) if (Array.isArray(table.indexes)) table.indexes.forEach((indexEntry, index)=>{
|
|
48413
|
+
validateDbTableIndex(indexEntry, `${tablePath}.indexes[${index}]`, issues);
|
|
48414
|
+
});
|
|
48415
|
+
else pushIssue(issues, `${tablePath}.indexes`, "must be an array");
|
|
48068
48416
|
}
|
|
48417
|
+
return issues;
|
|
48418
|
+
}
|
|
48419
|
+
function validateDbTableIndex(value, path, issues) {
|
|
48420
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48421
|
+
if (!Array.isArray(value.columns) || value.columns.some((c)=>"string" != typeof c)) pushIssue(issues, `${path}.columns`, "must be an array of strings");
|
|
48422
|
+
if (void 0 === value.type) pushIssue(issues, `${path}.type`, "is required");
|
|
48423
|
+
else if ("string" != typeof value.type || !DB_TABLE_INDEX_TYPES.has(value.type)) pushIssue(issues, `${path}.type`, 'must be "b_tree", "trigram", or "full_text"');
|
|
48424
|
+
}
|
|
48425
|
+
function validateDbSchemaColumn(value, path, issues) {
|
|
48426
|
+
if (!validators_isPlainObject(value)) return void pushIssue(issues, path, "must be an object");
|
|
48427
|
+
if (void 0 === value.name || "string" != typeof value.name || 0 === value.name.length) pushIssue(issues, `${path}.name`, "must be a non-empty string");
|
|
48428
|
+
else if (!IDENTIFIER_RE.test(value.name)) pushIssue(issues, `${path}.name`, "must be a valid Python identifier");
|
|
48429
|
+
if (void 0 === value.type) return void pushIssue(issues, `${path}.type`, "is required");
|
|
48430
|
+
if ("string" != typeof value.type) return void pushIssue(issues, `${path}.type`, "must be a string");
|
|
48431
|
+
const colType = value.type;
|
|
48432
|
+
if (DB_LINK_COLUMN_TYPES.has(colType)) {
|
|
48433
|
+
if (void 0 === value.target || "string" != typeof value.target || 0 === value.target.length) pushIssue(issues, `${path}.target`, "must be a non-empty string (target table python name)");
|
|
48434
|
+
else if (!IDENTIFIER_RE.test(value.target)) pushIssue(issues, `${path}.target`, "must be a valid Python identifier");
|
|
48435
|
+
} else if (!DB_BASIC_COLUMN_TYPES.has(colType)) pushIssue(issues, `${path}.type`, `must be a basic column type (${[
|
|
48436
|
+
...DB_BASIC_COLUMN_TYPES
|
|
48437
|
+
].join(", ")}) or ${[
|
|
48438
|
+
...DB_LINK_COLUMN_TYPES
|
|
48439
|
+
].join(", ")}`);
|
|
48440
|
+
if (void 0 !== value.client_hidden && "boolean" != typeof value.client_hidden) pushIssue(issues, `${path}.client_hidden`, "must be a boolean");
|
|
48441
|
+
if (void 0 !== value.admin_ui && null !== value.admin_ui) if (validators_isPlainObject(value.admin_ui)) {
|
|
48442
|
+
const ui = value.admin_ui;
|
|
48443
|
+
if (void 0 !== ui.width && "number" != typeof ui.width) pushIssue(issues, `${path}.admin_ui.width`, "must be a number");
|
|
48444
|
+
if (void 0 !== ui.order && "number" != typeof ui.order) pushIssue(issues, `${path}.admin_ui.order`, "must be a number");
|
|
48445
|
+
} else pushIssue(issues, `${path}.admin_ui`, "must be an object or null");
|
|
48446
|
+
if (void 0 !== value.indexes) {
|
|
48447
|
+
if (!Array.isArray(value.indexes)) return void pushIssue(issues, `${path}.indexes`, "must be an array");
|
|
48448
|
+
value.indexes.forEach((entry, i)=>{
|
|
48449
|
+
const idxPath = `${path}.indexes[${i}]`;
|
|
48450
|
+
if (!validators_isPlainObject(entry)) return void pushIssue(issues, idxPath, "must be an object");
|
|
48451
|
+
if (void 0 === entry.type) pushIssue(issues, `${idxPath}.type`, "is required");
|
|
48452
|
+
else if ("string" != typeof entry.type || !DB_TABLE_INDEX_TYPES.has(entry.type)) pushIssue(issues, `${idxPath}.type`, 'must be "b_tree", "trigram", or "full_text"');
|
|
48453
|
+
});
|
|
48454
|
+
}
|
|
48455
|
+
}
|
|
48456
|
+
function validateKnownServiceSources(services, path) {
|
|
48457
|
+
const issues = [];
|
|
48458
|
+
if (void 0 === services) return issues;
|
|
48459
|
+
if (!Array.isArray(services)) return issues;
|
|
48460
|
+
services.forEach((service, index)=>{
|
|
48461
|
+
if (!validators_isPlainObject(service) || "string" != typeof service.source) return;
|
|
48462
|
+
if (!KNOWN_RUNTIME_SERVICE_SOURCES.has(service.source)) pushIssue(issues, `${path}[${index}].source`, "must be a known runtime service source path");
|
|
48463
|
+
});
|
|
48464
|
+
return issues;
|
|
48069
48465
|
}
|
|
48070
|
-
|
|
48466
|
+
function validateServiceConfigs(services, path) {
|
|
48467
|
+
const issues = [];
|
|
48468
|
+
if (void 0 === services || !Array.isArray(services)) return issues;
|
|
48469
|
+
services.forEach((service, index)=>{
|
|
48470
|
+
if (!validators_isPlainObject(service) || "/runtime/services/anvil/users.yml" !== service.source) return;
|
|
48471
|
+
if (!validators_isPlainObject(service.client_config)) return;
|
|
48472
|
+
if (true === service.client_config.allow_remember_me && void 0 === service.client_config.remember_me_days) pushIssue(issues, `${path}[${index}].client_config.remember_me_days`, "must be set if allow_remember_me is true");
|
|
48473
|
+
});
|
|
48474
|
+
return issues;
|
|
48475
|
+
}
|
|
48476
|
+
function validateScheduledTasks(value, path) {
|
|
48477
|
+
const issues = [];
|
|
48478
|
+
if (void 0 === value) return issues;
|
|
48479
|
+
if (!Array.isArray(value)) {
|
|
48480
|
+
pushIssue(issues, path, "must be an array");
|
|
48481
|
+
return issues;
|
|
48482
|
+
}
|
|
48483
|
+
value.forEach((task, index)=>{
|
|
48484
|
+
const taskPath = `${path}[${index}]`;
|
|
48485
|
+
if (!validators_isPlainObject(task)) return void pushIssue(issues, taskPath, "must be an object");
|
|
48486
|
+
if ("string" != typeof task.job_id || 0 === task.job_id.length) pushIssue(issues, `${taskPath}.job_id`, "must be a non-empty string");
|
|
48487
|
+
validateIdentifier(task.task_name, `${taskPath}.task_name`, issues);
|
|
48488
|
+
if (!validators_isPlainObject(task.time_spec)) return void pushIssue(issues, `${taskPath}.time_spec`, "must be an object");
|
|
48489
|
+
const timeSpec = task.time_spec;
|
|
48490
|
+
if ("string" != typeof timeSpec.every || !SCHEDULE_EVERY_VALUES.has(timeSpec.every)) pushIssue(issues, `${taskPath}.time_spec.every`, 'must be one of "minute", "hour", "day", "week", or "month"');
|
|
48491
|
+
const interval = timeSpec.n;
|
|
48492
|
+
if (void 0 !== interval && ("number" != typeof interval || !Number.isInteger(interval) || interval <= 0)) pushIssue(issues, `${taskPath}.time_spec.n`, "must be a positive integer");
|
|
48493
|
+
if (void 0 !== timeSpec.at) if (validators_isPlainObject(timeSpec.at)) {
|
|
48494
|
+
const at = timeSpec.at;
|
|
48495
|
+
if (void 0 !== at.minute && !Number.isInteger(at.minute)) pushIssue(issues, `${taskPath}.time_spec.at.minute`, "must be an integer");
|
|
48496
|
+
if (void 0 !== at.hour && !Number.isInteger(at.hour)) pushIssue(issues, `${taskPath}.time_spec.at.hour`, "must be an integer");
|
|
48497
|
+
if (void 0 !== at.day && !Number.isInteger(at.day)) pushIssue(issues, `${taskPath}.time_spec.at.day`, "must be an integer");
|
|
48498
|
+
} else pushIssue(issues, `${taskPath}.time_spec.at`, "must be an object");
|
|
48499
|
+
});
|
|
48500
|
+
return issues;
|
|
48501
|
+
}
|
|
48502
|
+
const AnvilYamlSchema = looseObject({
|
|
48071
48503
|
name: dist_string(),
|
|
48072
|
-
package_name: dist_string(),
|
|
48504
|
+
package_name: optional(dist_string()),
|
|
48073
48505
|
allow_embedding: optional(dist_boolean()),
|
|
48074
48506
|
renamed: optional(dist_boolean()),
|
|
48075
48507
|
dependencies: optional(dist_array(any())),
|
|
48076
|
-
runtime_options:
|
|
48077
|
-
|
|
48078
|
-
|
|
48079
|
-
|
|
48080
|
-
|
|
48081
|
-
|
|
48082
|
-
|
|
48083
|
-
|
|
48084
|
-
|
|
48085
|
-
server_config: optional(any())
|
|
48086
|
-
}))),
|
|
48087
|
-
startup: optional(union([
|
|
48088
|
-
dist_object({
|
|
48089
|
-
module: dist_string(),
|
|
48090
|
-
type: dist_string()
|
|
48091
|
-
}),
|
|
48092
|
-
any()
|
|
48093
|
-
])),
|
|
48508
|
+
runtime_options: RuntimeOptionsSchema,
|
|
48509
|
+
services: optional(dist_array(AnvilServiceEntrySchema)),
|
|
48510
|
+
startup: optional(looseObject({
|
|
48511
|
+
type: picklist([
|
|
48512
|
+
"module",
|
|
48513
|
+
"form"
|
|
48514
|
+
]),
|
|
48515
|
+
module: optional(dist_string())
|
|
48516
|
+
})),
|
|
48094
48517
|
startup_form: optional(nullable(dist_string())),
|
|
48095
48518
|
db_schema: optional(any()),
|
|
48519
|
+
scheduled_tasks: optional(any()),
|
|
48096
48520
|
secrets: optional(any()),
|
|
48097
|
-
metadata: optional(
|
|
48521
|
+
metadata: optional(looseObject({
|
|
48522
|
+
title: optional(dist_string()),
|
|
48523
|
+
description: optional(dist_string()),
|
|
48524
|
+
logo_img: optional(dist_string())
|
|
48525
|
+
})),
|
|
48526
|
+
client_init_module: optional(dist_string())
|
|
48098
48527
|
});
|
|
48528
|
+
function formatValibotIssuePath(issue) {
|
|
48529
|
+
return getDotPath(issue) ?? "";
|
|
48530
|
+
}
|
|
48099
48531
|
function validateAnvilYaml(yamlContent) {
|
|
48100
|
-
if (yamlContent.includes("<<<<<<<") || yamlContent.includes(">>>>>>>")) {
|
|
48101
|
-
|
|
48102
|
-
|
|
48103
|
-
|
|
48532
|
+
if (yamlContent.includes("<<<<<<<") || yamlContent.includes(">>>>>>>")) return {
|
|
48533
|
+
ok: false,
|
|
48534
|
+
message: "anvil.yaml contains merge conflict markers",
|
|
48535
|
+
issues: [
|
|
48536
|
+
{
|
|
48537
|
+
path: "",
|
|
48538
|
+
message: "contains git merge conflict markers"
|
|
48539
|
+
}
|
|
48540
|
+
]
|
|
48541
|
+
};
|
|
48104
48542
|
try {
|
|
48105
48543
|
const data = js_yaml_load(yamlContent);
|
|
48106
48544
|
const result = safeParse(AnvilYamlSchema, data);
|
|
48107
|
-
if (!result.success) {
|
|
48108
|
-
|
|
48109
|
-
|
|
48110
|
-
|
|
48111
|
-
|
|
48112
|
-
|
|
48545
|
+
if (!result.success) return {
|
|
48546
|
+
ok: false,
|
|
48547
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48548
|
+
issues: result.issues.map((issue)=>({
|
|
48549
|
+
path: formatValibotIssuePath(issue),
|
|
48550
|
+
message: issue.message
|
|
48551
|
+
}))
|
|
48552
|
+
};
|
|
48553
|
+
const runtimeOptionIssues = validateRuntimeOptions(result.output.runtime_options, "runtime_options");
|
|
48554
|
+
if (runtimeOptionIssues.length > 0) return {
|
|
48555
|
+
ok: false,
|
|
48556
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48557
|
+
issues: runtimeOptionIssues
|
|
48558
|
+
};
|
|
48559
|
+
const serviceIssues = validateKnownServiceSources(result.output.services, "services");
|
|
48560
|
+
if (serviceIssues.length > 0) return {
|
|
48561
|
+
ok: false,
|
|
48562
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48563
|
+
issues: serviceIssues
|
|
48564
|
+
};
|
|
48565
|
+
const serviceConfigIssues = validateServiceConfigs(result.output.services, "services");
|
|
48566
|
+
if (serviceConfigIssues.length > 0) return {
|
|
48567
|
+
ok: false,
|
|
48568
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48569
|
+
issues: serviceConfigIssues
|
|
48570
|
+
};
|
|
48571
|
+
const scheduledTaskIssues = validateScheduledTasks(result.output.scheduled_tasks, "scheduled_tasks");
|
|
48572
|
+
if (scheduledTaskIssues.length > 0) return {
|
|
48573
|
+
ok: false,
|
|
48574
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48575
|
+
issues: scheduledTaskIssues
|
|
48576
|
+
};
|
|
48577
|
+
const dbIssues = validateDbSchema(result.output.db_schema, "db_schema");
|
|
48578
|
+
if (dbIssues.length > 0) return {
|
|
48579
|
+
ok: false,
|
|
48580
|
+
message: "anvil.yaml is invalid for Anvil",
|
|
48581
|
+
issues: dbIssues
|
|
48582
|
+
};
|
|
48583
|
+
return {
|
|
48584
|
+
ok: true
|
|
48585
|
+
};
|
|
48113
48586
|
} catch (error) {
|
|
48114
|
-
|
|
48115
|
-
|
|
48587
|
+
return {
|
|
48588
|
+
ok: false,
|
|
48589
|
+
message: "anvil.yaml is not valid YAML",
|
|
48590
|
+
issues: [
|
|
48591
|
+
{
|
|
48592
|
+
path: "",
|
|
48593
|
+
message: error instanceof Error ? error.message : String(error)
|
|
48594
|
+
}
|
|
48595
|
+
]
|
|
48596
|
+
};
|
|
48597
|
+
}
|
|
48598
|
+
}
|
|
48599
|
+
function inferValidationTarget(filePath) {
|
|
48600
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
48601
|
+
if ("anvil.yaml" === normalizedPath || normalizedPath.endsWith("/anvil.yaml")) return "anvil.yaml";
|
|
48602
|
+
const clientCodePrefix = "client_code/";
|
|
48603
|
+
if (normalizedPath.startsWith(clientCodePrefix) || normalizedPath.includes(`/${clientCodePrefix}`)) {
|
|
48604
|
+
const startIndex = normalizedPath.lastIndexOf(clientCodePrefix);
|
|
48605
|
+
const relativeToClientCode = normalizedPath.slice(startIndex + clientCodePrefix.length);
|
|
48606
|
+
if (relativeToClientCode.endsWith(".yaml")) return "form_template.yaml";
|
|
48607
|
+
}
|
|
48608
|
+
return null;
|
|
48609
|
+
}
|
|
48610
|
+
function resolveExistingPath(filePath) {
|
|
48611
|
+
const absolutePath = external_path_default().resolve(filePath);
|
|
48612
|
+
return external_fs_.existsSync(absolutePath) ? absolutePath : null;
|
|
48613
|
+
}
|
|
48614
|
+
function findAnvilAppRoot(filePath) {
|
|
48615
|
+
const existingPath = resolveExistingPath(filePath);
|
|
48616
|
+
if (!existingPath) return null;
|
|
48617
|
+
let currentDir = external_fs_.statSync(existingPath).isDirectory() ? existingPath : external_path_default().dirname(existingPath);
|
|
48618
|
+
let appRoot = null;
|
|
48619
|
+
while(true){
|
|
48620
|
+
if (external_fs_.existsSync(external_path_default().join(currentDir, "anvil.yaml"))) appRoot = currentDir;
|
|
48621
|
+
const parentDir = external_path_default().dirname(currentDir);
|
|
48622
|
+
if (parentDir === currentDir) return appRoot;
|
|
48623
|
+
currentDir = parentDir;
|
|
48624
|
+
}
|
|
48625
|
+
}
|
|
48626
|
+
function validatePath(filePath, yamlContent) {
|
|
48627
|
+
const target = inferValidationTarget(filePath);
|
|
48628
|
+
const existingPath = resolveExistingPath(filePath);
|
|
48629
|
+
const appRoot = findAnvilAppRoot(filePath);
|
|
48630
|
+
if (existingPath && !appRoot) return {
|
|
48631
|
+
ok: false,
|
|
48632
|
+
target: target ?? "unknown",
|
|
48633
|
+
message: "File is not inside an Anvil app",
|
|
48634
|
+
issues: [
|
|
48635
|
+
{
|
|
48636
|
+
path: "",
|
|
48637
|
+
message: "could not find an anvil.yaml app root for this file"
|
|
48638
|
+
}
|
|
48639
|
+
]
|
|
48640
|
+
};
|
|
48641
|
+
if ("anvil.yaml" === target) {
|
|
48642
|
+
if (existingPath && appRoot && external_path_default().resolve(existingPath) !== external_path_default().join(appRoot, "anvil.yaml")) return {
|
|
48643
|
+
ok: false,
|
|
48644
|
+
target,
|
|
48645
|
+
message: "File is not the app root anvil.yaml",
|
|
48646
|
+
issues: [
|
|
48647
|
+
{
|
|
48648
|
+
path: "",
|
|
48649
|
+
message: `expected ${external_path_default().join(appRoot, "anvil.yaml")}`
|
|
48650
|
+
}
|
|
48651
|
+
]
|
|
48652
|
+
};
|
|
48653
|
+
const result = validateAnvilYaml(yamlContent);
|
|
48654
|
+
return result.ok ? {
|
|
48655
|
+
ok: true,
|
|
48656
|
+
target
|
|
48657
|
+
} : {
|
|
48658
|
+
...result,
|
|
48659
|
+
target
|
|
48660
|
+
};
|
|
48661
|
+
}
|
|
48662
|
+
if ("form_template.yaml" === target) {
|
|
48663
|
+
if (existingPath && appRoot) {
|
|
48664
|
+
const clientCodeRoot = external_path_default().join(appRoot, "client_code");
|
|
48665
|
+
const relativeToClientCode = external_path_default().relative(clientCodeRoot, existingPath);
|
|
48666
|
+
const isInsideClientCode = relativeToClientCode.length > 0 && !relativeToClientCode.startsWith("..") && !external_path_default().isAbsolute(relativeToClientCode);
|
|
48667
|
+
if (!isInsideClientCode || !existingPath.endsWith(".yaml")) return {
|
|
48668
|
+
ok: false,
|
|
48669
|
+
target,
|
|
48670
|
+
message: "File is not a client_code YAML form",
|
|
48671
|
+
issues: [
|
|
48672
|
+
{
|
|
48673
|
+
path: "",
|
|
48674
|
+
message: `expected a .yaml file under ${clientCodeRoot}`
|
|
48675
|
+
}
|
|
48676
|
+
]
|
|
48677
|
+
};
|
|
48678
|
+
}
|
|
48679
|
+
const result = validateFormTemplate(yamlContent);
|
|
48680
|
+
return result.ok ? {
|
|
48681
|
+
ok: true,
|
|
48682
|
+
target
|
|
48683
|
+
} : {
|
|
48684
|
+
...result,
|
|
48685
|
+
target
|
|
48686
|
+
};
|
|
48116
48687
|
}
|
|
48688
|
+
return {
|
|
48689
|
+
ok: false,
|
|
48690
|
+
target: "unknown",
|
|
48691
|
+
message: "No validator for this path",
|
|
48692
|
+
issues: [
|
|
48693
|
+
{
|
|
48694
|
+
path: "",
|
|
48695
|
+
message: "supported paths are anvil.yaml, client_code/**/form_template.yaml, and client_code/**/*.yaml"
|
|
48696
|
+
}
|
|
48697
|
+
]
|
|
48698
|
+
};
|
|
48117
48699
|
}
|
|
48118
48700
|
function normalizeClientCodePath(relativePath) {
|
|
48119
48701
|
return relativePath.replace(/\\/g, "/");
|
|
@@ -48153,10 +48735,15 @@ var __webpack_exports__ = {};
|
|
|
48153
48735
|
try {
|
|
48154
48736
|
const content = await readFileContent(repoPath, relativePath, stagedOnly);
|
|
48155
48737
|
if ("yaml" === format) {
|
|
48156
|
-
|
|
48157
|
-
|
|
48158
|
-
|
|
48159
|
-
|
|
48738
|
+
const validationResult = validateFormTemplate(content);
|
|
48739
|
+
if (!validationResult.ok) {
|
|
48740
|
+
const firstIssue = validationResult.issues[0];
|
|
48741
|
+
const issueSummary = firstIssue ? `${formatValidationPath(firstIssue.path)}: ${firstIssue.message}` : void 0;
|
|
48742
|
+
return {
|
|
48743
|
+
ok: false,
|
|
48744
|
+
reason: issueSummary ? `${validationResult.message}. ${issueSummary}` : validationResult.message
|
|
48745
|
+
};
|
|
48746
|
+
}
|
|
48160
48747
|
const parsed = jsYaml.load(content);
|
|
48161
48748
|
const template = {
|
|
48162
48749
|
...parsed,
|
|
@@ -48830,10 +49417,11 @@ var __webpack_exports__ = {};
|
|
|
48830
49417
|
});
|
|
48831
49418
|
}
|
|
48832
49419
|
function buildFormDataFromTemplate(template, html, options) {
|
|
49420
|
+
const { class_name: _templateClassName, code: _templateCode, is_package: _templateIsPackage, ...templateRest } = template && "object" == typeof template ? template : {};
|
|
48833
49421
|
const formData = {
|
|
49422
|
+
...templateRest,
|
|
48834
49423
|
class_name: options.className,
|
|
48835
49424
|
code: options.code,
|
|
48836
|
-
...template,
|
|
48837
49425
|
is_package: options.isPackage
|
|
48838
49426
|
};
|
|
48839
49427
|
if (void 0 !== html) {
|
|
@@ -49043,7 +49631,8 @@ var __webpack_exports__ = {};
|
|
|
49043
49631
|
requirements: currentRequirements
|
|
49044
49632
|
}
|
|
49045
49633
|
};
|
|
49046
|
-
|
|
49634
|
+
const validationResult = validateAnvilYaml(yamlContent);
|
|
49635
|
+
if (!validationResult.ok) throw new Error("anvil.yaml validation failed - see errors above");
|
|
49047
49636
|
const changes = [];
|
|
49048
49637
|
for (const [key, value] of Object.entries(parsedYaml))if (!previousYaml || !deepEqual(previousYaml[key], value)) changes.push({
|
|
49049
49638
|
key,
|
|
@@ -49538,6 +50127,19 @@ var __webpack_exports__ = {};
|
|
|
49538
50127
|
};
|
|
49539
50128
|
}
|
|
49540
50129
|
}
|
|
50130
|
+
function stripNonSemanticYamlFields(yamlFile, parsed) {
|
|
50131
|
+
const normalized = {
|
|
50132
|
+
...parsed ?? {}
|
|
50133
|
+
};
|
|
50134
|
+
delete normalized.is_package;
|
|
50135
|
+
const normalizedPath = yamlFile.replace(/\\/g, "/");
|
|
50136
|
+
const isFormTemplate = normalizedPath.startsWith("client_code/") && (normalizedPath.endsWith("/form_template.yaml") || /^client_code\/[^/]+\.ya?ml$/i.test(normalizedPath) && !normalizedPath.endsWith("anvil.yaml"));
|
|
50137
|
+
if (isFormTemplate) {
|
|
50138
|
+
delete normalized.class_name;
|
|
50139
|
+
delete normalized.code;
|
|
50140
|
+
}
|
|
50141
|
+
return normalized;
|
|
50142
|
+
}
|
|
49541
50143
|
class SyncManager extends Emitter_Emitter {
|
|
49542
50144
|
config;
|
|
49543
50145
|
syncInProgress = false;
|
|
@@ -49658,8 +50260,8 @@ var __webpack_exports__ = {};
|
|
|
49658
50260
|
}
|
|
49659
50261
|
const workingParsed = js_yaml_load(workingContent);
|
|
49660
50262
|
const headParsed = js_yaml_load(headContent);
|
|
49661
|
-
const
|
|
49662
|
-
const
|
|
50263
|
+
const workingForCompare = stripNonSemanticYamlFields(yamlFile, workingParsed);
|
|
50264
|
+
const headForCompare = stripNonSemanticYamlFields(yamlFile, headParsed);
|
|
49663
50265
|
if (deepEqual(workingForCompare, headForCompare)) {
|
|
49664
50266
|
logger_logger.verbose(chalk_source.gray(` Discarding formatting-only change: ${yamlFile}`));
|
|
49665
50267
|
await this.config.gitService.checkout([
|
|
@@ -51034,6 +51636,33 @@ var __webpack_exports__ = {};
|
|
|
51034
51636
|
function getUsernameConfigKey(appId) {
|
|
51035
51637
|
return `anvil.auth.${appId}.username`;
|
|
51036
51638
|
}
|
|
51639
|
+
function shellQuote(value) {
|
|
51640
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
51641
|
+
}
|
|
51642
|
+
function shellQuoteDouble(value) {
|
|
51643
|
+
return `"${value.replace(/(["$`])/g, "\\$1")}"`;
|
|
51644
|
+
}
|
|
51645
|
+
function getDefaultCliPath(options) {
|
|
51646
|
+
const servicesDir = options?.servicesDir ?? __dirname;
|
|
51647
|
+
const pathExists = options?.pathExists ?? external_fs_default().existsSync;
|
|
51648
|
+
const candidates = [
|
|
51649
|
+
external_path_default().resolve(servicesDir, "..", "cli.js"),
|
|
51650
|
+
external_path_default().resolve(servicesDir, "..", "cli.ts")
|
|
51651
|
+
];
|
|
51652
|
+
return candidates.find((candidate)=>pathExists(candidate));
|
|
51653
|
+
}
|
|
51654
|
+
function getGitCredentialHelperCommand(options) {
|
|
51655
|
+
const platform = options?.platform ?? process.platform;
|
|
51656
|
+
const execPath = options?.execPath ?? process.execPath;
|
|
51657
|
+
const cliPath = options?.cliPath ?? getDefaultCliPath() ?? process.argv[1];
|
|
51658
|
+
if (!execPath || !cliPath) return "!anvil git-credential";
|
|
51659
|
+
if ("win32" === platform) {
|
|
51660
|
+
const resolvedExecPath = external_path_default().win32.resolve(execPath);
|
|
51661
|
+
const resolvedCliPath = external_path_default().win32.resolve(cliPath);
|
|
51662
|
+
return `!${shellQuoteDouble(resolvedExecPath)} ${shellQuoteDouble(resolvedCliPath)} git-credential`;
|
|
51663
|
+
}
|
|
51664
|
+
return `!${shellQuote(external_path_default().resolve(execPath))} ${shellQuote(external_path_default().resolve(cliPath))} git-credential`;
|
|
51665
|
+
}
|
|
51037
51666
|
async function getLocalConfigValue(repoPath, key) {
|
|
51038
51667
|
try {
|
|
51039
51668
|
const value = (await esm_default(repoPath).raw([
|
|
@@ -51102,7 +51731,7 @@ var __webpack_exports__ = {};
|
|
|
51102
51731
|
"--local",
|
|
51103
51732
|
"--add",
|
|
51104
51733
|
`credential.${scope}.helper`,
|
|
51105
|
-
|
|
51734
|
+
getGitCredentialHelperCommand()
|
|
51106
51735
|
]);
|
|
51107
51736
|
await git.raw([
|
|
51108
51737
|
"config",
|
|
@@ -53466,6 +54095,44 @@ var __webpack_exports__ = {};
|
|
|
53466
54095
|
program.command("update").description("Update anvil to the latest version").alias("u").action(async ()=>{
|
|
53467
54096
|
await handleUpdateCommand();
|
|
53468
54097
|
});
|
|
54098
|
+
program.command("validate").description("Validate YAML based on path (anvil.yaml or client_code/**/*.yaml)").argument("<file>", "Path to YAML file").action((file)=>{
|
|
54099
|
+
let yamlContent;
|
|
54100
|
+
try {
|
|
54101
|
+
yamlContent = (0, external_fs_.readFileSync)(file, "utf8");
|
|
54102
|
+
} catch (error) {
|
|
54103
|
+
logger_logger.error(`Failed to read ${file}: ${error.message}`);
|
|
54104
|
+
process.exitCode = 1;
|
|
54105
|
+
return;
|
|
54106
|
+
}
|
|
54107
|
+
const result = validatePath(file, yamlContent);
|
|
54108
|
+
if (result.ok) {
|
|
54109
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(true, {
|
|
54110
|
+
data: {
|
|
54111
|
+
file,
|
|
54112
|
+
type: result.target,
|
|
54113
|
+
valid: true
|
|
54114
|
+
}
|
|
54115
|
+
});
|
|
54116
|
+
else logger_logger.success(`${file} is valid (${result.target})`);
|
|
54117
|
+
return;
|
|
54118
|
+
}
|
|
54119
|
+
if (getGlobalOutputConfig().jsonMode) logJsonResult(false, {
|
|
54120
|
+
error: result.message,
|
|
54121
|
+
data: {
|
|
54122
|
+
file,
|
|
54123
|
+
type: result.target,
|
|
54124
|
+
issues: result.issues
|
|
54125
|
+
}
|
|
54126
|
+
});
|
|
54127
|
+
else {
|
|
54128
|
+
logger_logger.error(`${file} failed validation: ${result.message}`);
|
|
54129
|
+
for (const issue of result.issues){
|
|
54130
|
+
const issuePath = issue.path.length > 0 ? issue.path : "root";
|
|
54131
|
+
logger_logger.error(` - ${issuePath}: ${issue.message}`);
|
|
54132
|
+
}
|
|
54133
|
+
}
|
|
54134
|
+
process.exitCode = 1;
|
|
54135
|
+
});
|
|
53469
54136
|
if (watchCommand) {
|
|
53470
54137
|
const watchOptions = watchCommand.options.map((opt)=>{
|
|
53471
54138
|
const flags = opt.flags;
|