@antelopejs/dms 0.12.0 → 0.13.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/CHANGELOG.md +66 -0
- package/README.md +1 -1
- package/data/dbip-country-lite.mmdb +0 -0
- package/data/module-store/catalog.json +0 -6
- package/dist/config/internal/state.d.ts +5 -0
- package/dist/config/internal/state.js.map +1 -1
- package/dist/frontend-module.js +1 -0
- package/dist/frontend-module.js.map +1 -1
- package/dist/implementations/dms/page/quick-action-access.js +4 -1
- package/dist/implementations/dms/page/quick-action-access.js.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/store/operations.js +3 -1
- package/dist/store/operations.js.map +1 -1
- package/dist/store/runtime.d.ts +1 -0
- package/dist/store/runtime.js +9 -0
- package/dist/store/runtime.js.map +1 -1
- package/dist/utils/ensure-default-tenant.js +7 -2
- package/dist/utils/ensure-default-tenant.js.map +1 -1
- package/frontend-vue/dms.frontend.ts +2 -0
- package/frontend-vue/layers/dms-auth/app/build/components/AuthFormAlert.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/build/composables/useAuthFormError.ts +11 -11
- package/frontend-vue/layers/dms-auth/app/composables/useOAuthErrorToast.ts +3 -8
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/accounts.vue +3 -2
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/login.vue +3 -1
- package/frontend-vue/layers/dms-auth/server/oauth.ts +5 -3
- package/frontend-vue/layers/dms-core/app/build/composables/api-error.ts +145 -0
- package/frontend-vue/layers/dms-core/app/build/composables/auth/usePermissionPreview.ts +5 -1
- package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/page/useSiteLayout.ts +6 -5
- package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -10
- package/frontend-vue/layers/dms-core/app/composables/useApiErrorPresenter.ts +159 -0
- package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +14 -26
- package/frontend-vue/layers/dms-core/app/utils/composedText.ts +11 -4
- package/frontend-vue/layers/dms-core/app/utils/row-action-rule-evaluator.ts +8 -36
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +1 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +1 -0
- package/frontend-vue/layers/dms-core/shared/types/runtime-config.d.ts +6 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidePanel.vue +3 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +14 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +3 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +3 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +6 -6
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityEmail.vue +7 -9
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityPassword.vue +4 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +6 -7
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +2 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +14 -34
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +3 -11
- package/frontend-vue/layers/dms-layout/app/build/utils/github-repository.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +9 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +13 -28
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +2 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/settings/roles.vue +5 -2
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +4 -4
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +2 -4
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +2 -4
- package/frontend-vue/layers/dms-layout/server/render-token.ts +5 -3
- package/frontend-vue/layers/dms-ui/app/build/components/blocks/BlockStatus.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/actionTargetRequests.ts +6 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/chart/groupPoints.ts +51 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/actionError.ts +10 -139
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/useActionConfirm.ts +2 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/defaultPrimitiveTypes.ts +6 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formState.ts +2 -12
- package/frontend-vue/layers/dms-ui/app/build/composables/page/blockAnchors.ts +2 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableConfigClipboard.ts +7 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/tableRowActionBulkRequests.ts +11 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/tableRowActionValues.ts +1 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/tableViewGrouping.ts +9 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useExpandedRowDetails.ts +4 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRecordLink.ts +8 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +53 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +7 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/writeError.ts +9 -5
- package/frontend-vue/layers/dms-ui/app/build/utils/fetchNotFound.ts +4 -3
- package/frontend-vue/layers/dms-ui/app/components/block-preview/BlockPreview.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/BannerBlock.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +3 -2
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +24 -16
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -6
- package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +2 -4
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +6 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +5 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/ImagePresentation.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -6
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +2 -6
- package/frontend-vue/layers/dms-ui/app/components/form/components/imageItems.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/components/guard/Guard.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +13 -9
- package/frontend-vue/layers/dms-ui/app/composables/chart/types.ts +11 -1
- package/frontend-vue/layers/dms-ui/app/composables/confirm/types.ts +6 -6
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +14 -21
- package/frontend-vue/layers/dms-ui/app/composables/global/keyboardPlatform.ts +36 -9
- package/frontend-vue/layers/dms-ui/app/composables/useExportJob.ts +13 -59
- package/frontend-vue/layers/dms-ui/app/plugins/kbd-platform.ts +13 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +1 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +1 -0
- package/frontend-vue/package.json +3 -5
- package/frontend-vue/scripts/check-api-error-presentation.mjs +242 -0
- package/frontend-vue/scripts/check-invariants.mjs +23 -0
- package/frontend-vue/scripts/codemods/api-error-presenter.mjs +196 -0
- package/frontend-vue/scripts/test-api-error-presentation.mjs +93 -0
- package/package.json +23 -7
- package/skills/dms-module/REFERENCE.md +3 -1
- package/skills/dms-module/SKILL.md +7 -2
- package/skills/dms-project/SKILL.md +9 -3
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import {
|
|
6
|
+
apiErrorSources,
|
|
7
|
+
apiErrorScripts,
|
|
8
|
+
analyzeApiErrorScript,
|
|
9
|
+
} from "../check-api-error-presentation.mjs";
|
|
10
|
+
|
|
11
|
+
const require = createRequire(resolve(process.cwd(), "package.json"));
|
|
12
|
+
const tools = {
|
|
13
|
+
ts: require("typescript"),
|
|
14
|
+
parse: require("vue/compiler-sfc").parse,
|
|
15
|
+
};
|
|
16
|
+
const { ts } = tools;
|
|
17
|
+
|
|
18
|
+
function translatedKey(expression) {
|
|
19
|
+
if (
|
|
20
|
+
!expression ||
|
|
21
|
+
!ts.isCallExpression(expression) ||
|
|
22
|
+
!["t", "processI18n"].includes(expression.expression.getText())
|
|
23
|
+
)
|
|
24
|
+
return null;
|
|
25
|
+
const key = expression.arguments[0];
|
|
26
|
+
return expression.arguments.length === 1 && key && ts.isStringLiteral(key)
|
|
27
|
+
? key.text
|
|
28
|
+
: null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function literalKey(payload) {
|
|
32
|
+
if (!payload || !ts.isObjectLiteralExpression(payload)) return null;
|
|
33
|
+
const allowed = new Set(["title", "description", "color", "icon"]);
|
|
34
|
+
if (
|
|
35
|
+
payload.properties.some(
|
|
36
|
+
(property) =>
|
|
37
|
+
!ts.isPropertyAssignment(property) ||
|
|
38
|
+
!allowed.has(property.name.getText()) ||
|
|
39
|
+
(["title", "description"].includes(property.name.getText())
|
|
40
|
+
? translatedKey(property.initializer) === null
|
|
41
|
+
: !(
|
|
42
|
+
ts.isStringLiteral(property.initializer) ||
|
|
43
|
+
(property.name.getText() === "color" &&
|
|
44
|
+
/^Color\.(error|warning|info)$/.test(
|
|
45
|
+
property.initializer.getText(),
|
|
46
|
+
))
|
|
47
|
+
)),
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
return null;
|
|
51
|
+
const title =
|
|
52
|
+
payload.properties.find(
|
|
53
|
+
(property) => property.name.getText() === "description",
|
|
54
|
+
)?.initializer ??
|
|
55
|
+
payload.properties.find((property) => property.name.getText() === "title")
|
|
56
|
+
?.initializer;
|
|
57
|
+
return translatedKey(title);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function composableScope(node) {
|
|
61
|
+
let outer = null;
|
|
62
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
63
|
+
if (
|
|
64
|
+
(ts.isFunctionDeclaration(parent) ||
|
|
65
|
+
ts.isFunctionExpression(parent) ||
|
|
66
|
+
ts.isArrowFunction(parent)) &&
|
|
67
|
+
ts.isBlock(parent.body)
|
|
68
|
+
)
|
|
69
|
+
outer = parent;
|
|
70
|
+
}
|
|
71
|
+
const name = outer?.name ?? outer?.parent?.name;
|
|
72
|
+
return name && ts.isIdentifier(name) && /^use[A-Z]/.test(name.text)
|
|
73
|
+
? outer.body
|
|
74
|
+
: null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Rewrites supported setup/composable catches and returns unsupported cases. */
|
|
78
|
+
export function migrateApiErrorSource(file, source) {
|
|
79
|
+
const edits = [];
|
|
80
|
+
const manual = [];
|
|
81
|
+
for (const script of apiErrorScripts(file, source, tools)) {
|
|
82
|
+
const calls = analyzeApiErrorScript(script.source, file, ts);
|
|
83
|
+
let capture = false;
|
|
84
|
+
for (const call of calls) {
|
|
85
|
+
const fallbackKey = literalKey(call.payload);
|
|
86
|
+
const standalone = ts.isExpressionStatement(call.node.parent);
|
|
87
|
+
const safeCatch = call.failure && /^[A-Za-z_$][\w$]*$/.test(call.failure);
|
|
88
|
+
const collision = /\b(?:presentError|useApiErrorPresenter)\b/.test(
|
|
89
|
+
script.source,
|
|
90
|
+
);
|
|
91
|
+
const hasSetup = file.endsWith(".vue")
|
|
92
|
+
? script.isSetup
|
|
93
|
+
: composableScope(call.node);
|
|
94
|
+
if (
|
|
95
|
+
fallbackKey &&
|
|
96
|
+
standalone &&
|
|
97
|
+
safeCatch &&
|
|
98
|
+
hasSetup &&
|
|
99
|
+
call.method === "add" &&
|
|
100
|
+
!collision
|
|
101
|
+
) {
|
|
102
|
+
edits.push({
|
|
103
|
+
start: script.offset + call.node.getStart(),
|
|
104
|
+
end: script.offset + call.node.end,
|
|
105
|
+
text: `presentError(${call.failure}, { fallbackKey: ${JSON.stringify(fallbackKey)} })`,
|
|
106
|
+
});
|
|
107
|
+
capture = true;
|
|
108
|
+
} else {
|
|
109
|
+
manual.push({
|
|
110
|
+
file,
|
|
111
|
+
line: source
|
|
112
|
+
.slice(0, script.offset + call.node.getStart())
|
|
113
|
+
.split("\n").length,
|
|
114
|
+
reason: "manual-error-presentation",
|
|
115
|
+
expression: call.node.getText(call.tree),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (capture) {
|
|
120
|
+
const tree = ts.createSourceFile(
|
|
121
|
+
file,
|
|
122
|
+
script.source,
|
|
123
|
+
ts.ScriptTarget.Latest,
|
|
124
|
+
true,
|
|
125
|
+
);
|
|
126
|
+
const imports = tree.statements.filter(ts.isImportDeclaration);
|
|
127
|
+
const offset = imports.at(-1)?.end ?? 0;
|
|
128
|
+
const declaration = file.endsWith(".vue")
|
|
129
|
+
? "\nconst { presentError } = useApiErrorPresenter();\n"
|
|
130
|
+
: `\nimport { useApiErrorPresenter } from "#dms-core/app/composables/useApiErrorPresenter";\n`;
|
|
131
|
+
if (file.endsWith(".vue"))
|
|
132
|
+
edits.push({
|
|
133
|
+
start: script.offset + offset,
|
|
134
|
+
end: script.offset + offset,
|
|
135
|
+
text: declaration,
|
|
136
|
+
});
|
|
137
|
+
else {
|
|
138
|
+
const scopes = new Set();
|
|
139
|
+
for (const call of calls) {
|
|
140
|
+
if (
|
|
141
|
+
!edits.some(
|
|
142
|
+
(edit) => edit.start === script.offset + call.node.getStart(),
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
continue;
|
|
146
|
+
const outer = composableScope(call.node);
|
|
147
|
+
if (outer) scopes.add(outer.getStart() + 1);
|
|
148
|
+
}
|
|
149
|
+
for (const start of scopes)
|
|
150
|
+
edits.push({
|
|
151
|
+
start: script.offset + start,
|
|
152
|
+
end: script.offset + start,
|
|
153
|
+
text: "\nconst { presentError } = useApiErrorPresenter();\n",
|
|
154
|
+
});
|
|
155
|
+
edits.push({
|
|
156
|
+
start: script.offset + offset,
|
|
157
|
+
end: script.offset + offset,
|
|
158
|
+
text: declaration,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
let output = source;
|
|
164
|
+
for (const edit of edits.sort((a, b) => b.start - a.start))
|
|
165
|
+
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
166
|
+
return { output, rewritten: output !== source, manual };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Dry-run by default; writes only source cases accepted by the bounded migration. */
|
|
170
|
+
export function migrateApiErrorFile(file, write = false) {
|
|
171
|
+
const result = migrateApiErrorSource(file, readFileSync(file, "utf8"));
|
|
172
|
+
if (write && result.rewritten) writeFileSync(file, result.output);
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
process.argv[1] &&
|
|
178
|
+
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
179
|
+
) {
|
|
180
|
+
const roots = process.argv
|
|
181
|
+
.slice(2)
|
|
182
|
+
.filter((argument) => argument !== "--write");
|
|
183
|
+
if (!roots.length)
|
|
184
|
+
throw new Error(
|
|
185
|
+
"Pass app directories. Add --write to apply safe rewrites. Manual cases are JSONL on stdout.",
|
|
186
|
+
);
|
|
187
|
+
for (const file of roots.flatMap(apiErrorSources)) {
|
|
188
|
+
const result = migrateApiErrorFile(file, process.argv.includes("--write"));
|
|
189
|
+
for (const entry of result.manual)
|
|
190
|
+
process.stdout.write(`${JSON.stringify(entry)}\n`);
|
|
191
|
+
if (result.rewritten)
|
|
192
|
+
process.stderr.write(
|
|
193
|
+
`${process.argv.includes("--write") ? "rewrote" : "would rewrite"} ${file}\n`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import {
|
|
4
|
+
analyzeApiErrorScript,
|
|
5
|
+
apiErrorScripts,
|
|
6
|
+
} from "./check-api-error-presentation.mjs";
|
|
7
|
+
import { migrateApiErrorSource } from "./codemods/api-error-presenter.mjs";
|
|
8
|
+
import { parse } from "vue/compiler-sfc";
|
|
9
|
+
|
|
10
|
+
const rejected = [
|
|
11
|
+
`try { await save() } catch (error) { toast.add({ title: t('save.failed'), color: 'error' }) }`,
|
|
12
|
+
`save().catch(error => { toast.update(id, { color: 'error' }) })`,
|
|
13
|
+
`save().then(done, error => { toast.add({ color: Color.error }) })`,
|
|
14
|
+
`const notices = useToast(); try {} catch (error) { notices.add({ color: 'warning' }) }`,
|
|
15
|
+
`const { add: notify } = useToast(); try {} catch (error) { notify({ color: FAILURE_COLOR }) }`,
|
|
16
|
+
`const notices = useToast(); const alias = notices; try {} catch (error) { alias.update(id, payload) }`,
|
|
17
|
+
`import { ERROR_COLOR } from './colors'; try {} catch (error) { toast.add({ color: ERROR_COLOR }) }`,
|
|
18
|
+
`const payload = { color: 'error' }; try {} catch (error) { toast.add(payload) }`,
|
|
19
|
+
`save().catch(error => { toast.add({ title: t('save.failed') }) })`,
|
|
20
|
+
];
|
|
21
|
+
for (const source of rejected)
|
|
22
|
+
assert.equal(
|
|
23
|
+
analyzeApiErrorScript(source, "fixture.ts", ts).length,
|
|
24
|
+
1,
|
|
25
|
+
source,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const allowed = [
|
|
29
|
+
`try { await save(); toast.add({ color: 'success' }) } catch (error) { presentError(error, { fallbackKey: 'save.failed' }) }`,
|
|
30
|
+
`try { await track() } catch {} finally { loading.value = false }`,
|
|
31
|
+
`toast.add({ color: 'info', title: t('product.notice') })`,
|
|
32
|
+
`save().catch(() => { toast.add({ color: 'success' }) })`,
|
|
33
|
+
];
|
|
34
|
+
for (const source of allowed)
|
|
35
|
+
assert.equal(
|
|
36
|
+
analyzeApiErrorScript(source, "fixture.ts", ts).length,
|
|
37
|
+
0,
|
|
38
|
+
source,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const vue = `<script setup lang="ts">
|
|
42
|
+
const toast = useToast();
|
|
43
|
+
async function save() {
|
|
44
|
+
try { await write() } catch (failure) { toast.add({ title: t("save.failed"), color: "error" }); }
|
|
45
|
+
}
|
|
46
|
+
</script><template><button @click="save">Save</button></template>`;
|
|
47
|
+
const migrated = migrateApiErrorSource("fixture.vue", vue);
|
|
48
|
+
assert.ok(migrated.rewritten);
|
|
49
|
+
assert.deepEqual(migrated.manual, []);
|
|
50
|
+
assert.ok(
|
|
51
|
+
migrated.output.includes(
|
|
52
|
+
'presentError(failure, { fallbackKey: "save.failed" })',
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
assert.equal(
|
|
56
|
+
migrateApiErrorSource("fixture.vue", migrated.output).rewritten,
|
|
57
|
+
false,
|
|
58
|
+
);
|
|
59
|
+
for (const script of apiErrorScripts("fixture.vue", migrated.output, { parse }))
|
|
60
|
+
assert.equal(
|
|
61
|
+
analyzeApiErrorScript(script.source, "fixture.vue", ts).length,
|
|
62
|
+
0,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const composable = `export function useSave() { return async () => { try { await save() } catch (error) { toast.add({ title: t('save.failed'), color: 'error' }); } } }`;
|
|
66
|
+
const converted = migrateApiErrorSource("fixture.ts", composable);
|
|
67
|
+
assert.ok(
|
|
68
|
+
converted.output.indexOf("const { presentError }") <
|
|
69
|
+
converted.output.indexOf("return async"),
|
|
70
|
+
);
|
|
71
|
+
assert.ok(converted.output.includes("import { useApiErrorPresenter }"));
|
|
72
|
+
|
|
73
|
+
for (const source of [
|
|
74
|
+
`async function save() { try {} catch (error) { toast.add({ title: t('save.failed'), color: 'error' }); } }`,
|
|
75
|
+
`export function useSave() { try {} catch { toast.add({ title: t('save.failed'), color: 'error' }); } }`,
|
|
76
|
+
`export function useSave() { try {} catch (error) { toast.update(id, { title: t('save.failed'), color: 'error' }); } }`,
|
|
77
|
+
`export function useSave() { try {} catch (error) { toast.add({ title: t(dynamic), color: 'error' }); } }`,
|
|
78
|
+
`export function useSave() { try {} catch (error) { toast.add({ title: t('save.failed'), color: color() }); } }`,
|
|
79
|
+
`export function useSave() { try {} catch (error) { toast.add({ title: t('save.failed'), icon: icon(), color: 'error' }); } }`,
|
|
80
|
+
`<script>try {} catch (error) { toast.add({ title: t('save.failed'), color: 'error' }); }</script>`,
|
|
81
|
+
]) {
|
|
82
|
+
const file = source.startsWith("<script") ? "manual.vue" : "manual.ts";
|
|
83
|
+
const result = migrateApiErrorSource(file, source);
|
|
84
|
+
assert.equal(result.output, source);
|
|
85
|
+
assert.deepEqual(Object.keys(result.manual[0]), [
|
|
86
|
+
"file",
|
|
87
|
+
"line",
|
|
88
|
+
"reason",
|
|
89
|
+
"expression",
|
|
90
|
+
]);
|
|
91
|
+
assert.equal(result.manual[0].reason, "manual-error-presentation");
|
|
92
|
+
}
|
|
93
|
+
console.log("PASS API error invariant and codemod fixtures");
|
package/package.json
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "AntelopeJS DMS. Plug in your PostgreSQL, MongoDB or RethinkDB database and your coding agent builds a back office and analytics dashboard as typed TypeScript. Real-time updates on the data the DMS manages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
7
7
|
"dms",
|
|
8
8
|
"dashboard",
|
|
9
9
|
"admin",
|
|
10
|
+
"admin-panel",
|
|
11
|
+
"admin-dashboard",
|
|
10
12
|
"backoffice",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
+
"back-office",
|
|
14
|
+
"internal-tools",
|
|
15
|
+
"typescript",
|
|
16
|
+
"open-source",
|
|
17
|
+
"self-hosted",
|
|
18
|
+
"postgresql",
|
|
19
|
+
"mongodb",
|
|
20
|
+
"rethinkdb",
|
|
21
|
+
"vue",
|
|
22
|
+
"inertia"
|
|
13
23
|
],
|
|
14
24
|
"homepage": "https://dms.antelopejs.com/docs",
|
|
15
25
|
"bugs": {
|
|
@@ -61,11 +71,11 @@
|
|
|
61
71
|
"2fa": "^0.1.2",
|
|
62
72
|
"@antelopejs/interface-api": ">=0.0.15 <1.0.0",
|
|
63
73
|
"@antelopejs/interface-api-util": ">=0.1.3 <1.0.0",
|
|
64
|
-
"@antelopejs/interface-core": ">=0.2.
|
|
74
|
+
"@antelopejs/interface-core": ">=0.2.6 <1.0.0",
|
|
65
75
|
"@antelopejs/interface-data-api": ">=0.5.0 <1.0.0",
|
|
66
76
|
"@antelopejs/interface-database": ">=0.1.11 <1.0.0",
|
|
67
77
|
"@antelopejs/interface-database-decorators": ">=0.1.10 <1.0.0",
|
|
68
|
-
"@antelopejs/interface-dms": ">=0.10.
|
|
78
|
+
"@antelopejs/interface-dms": ">=0.10.2 <0.11.0",
|
|
69
79
|
"@antelopejs/interface-email": ">=0.0.6 <1.0.0",
|
|
70
80
|
"@antelopejs/interface-file-storage": ">=0.1.4 <1.0.0",
|
|
71
81
|
"archiver": "^7.0.1",
|
|
@@ -104,7 +114,8 @@
|
|
|
104
114
|
"release-it-changelogen": "^0.1.0",
|
|
105
115
|
"rimraf": "^6.1.3",
|
|
106
116
|
"tsc-alias": "^1.9.1",
|
|
107
|
-
"typescript": "^6.0.3"
|
|
117
|
+
"typescript": "^6.0.3",
|
|
118
|
+
"vue": "^3.5.18"
|
|
108
119
|
},
|
|
109
120
|
"exports": {
|
|
110
121
|
".": {
|
|
@@ -112,6 +123,8 @@
|
|
|
112
123
|
"default": "./dist/index.js"
|
|
113
124
|
},
|
|
114
125
|
"./package.json": "./package.json",
|
|
126
|
+
"./checks/api-error-presentation": "./frontend-vue/scripts/check-api-error-presentation.mjs",
|
|
127
|
+
"./codemods/api-error-presenter": "./frontend-vue/scripts/codemods/api-error-presenter.mjs",
|
|
115
128
|
"./interfaces/*": null
|
|
116
129
|
},
|
|
117
130
|
"publishConfig": {
|
|
@@ -135,6 +148,8 @@
|
|
|
135
148
|
"build": "pnpm --filter @antelopejs/interface-dms build && rimraf dist && tsc && tsc-alias",
|
|
136
149
|
"dev": "ajs project run --inspect -w -p playground",
|
|
137
150
|
"frontend:dev": "pnpm --dir playground frontend:dev",
|
|
151
|
+
"frontend:build": "pnpm --dir playground frontend:build",
|
|
152
|
+
"frontend:start": "pnpm --dir playground frontend:start",
|
|
138
153
|
"typecheck": "node scripts/unless-prebuilt.mjs pnpm --filter @antelopejs/interface-dms build && tsc --noEmit",
|
|
139
154
|
"test": "node scripts/unless-prebuilt.mjs pnpm run build && NODE_OPTIONS=\"${NODE_OPTIONS:+$NODE_OPTIONS }--require=\\\"$PWD/scripts/backend-runtime-boundaries.cjs\\\"\" ajs module test .",
|
|
140
155
|
"test:unit": "node scripts/unless-prebuilt.mjs pnpm run build && node scripts/backend-checks.cjs && DMS_TEST_DIR=dist/test/unit NODE_OPTIONS=\"${NODE_OPTIONS:+$NODE_OPTIONS }--require=\\\"$PWD/scripts/backend-runtime-boundaries.cjs\\\"\" ajs module test .",
|
|
@@ -146,6 +161,7 @@
|
|
|
146
161
|
"knip:all": "knip",
|
|
147
162
|
"docs:catalog": "node scripts/docs-catalog.mjs",
|
|
148
163
|
"docs:catalog:check": "node scripts/docs-catalog.mjs --check",
|
|
164
|
+
"check:frontend-declarations": "node scripts/check-frontend-declarations.mjs",
|
|
149
165
|
"docs:screenshots": "node scripts/docs-screenshots.mjs",
|
|
150
166
|
"realtime:tabs-check": "node scripts/realtime-tabs-check.mjs"
|
|
151
167
|
}
|
|
@@ -33,6 +33,7 @@ first-party modules follow `dms-<name>` purely for discoverability.
|
|
|
33
33
|
```jsonc
|
|
34
34
|
{
|
|
35
35
|
"main": "dist/index.js", "types": "dist/index.d.ts",
|
|
36
|
+
"dmsFrontend": "dist/frontend-module.js", // exports frontendModule(config, { dev }) (omit if no frontend)
|
|
36
37
|
"antelopeJs": {
|
|
37
38
|
"baseUrl": "dist",
|
|
38
39
|
"implements": ["<your-interface-package>"], // the interface THIS module provides (omit/[] if none)
|
|
@@ -80,7 +81,8 @@ AntelopeJS — see the **ajs-author-interface** and **ajs-consume-interface** sk
|
|
|
80
81
|
`frontend-vue/` (components registered from the root `dms.frontend.ts`), `skills/<name>/SKILL.md`,
|
|
81
82
|
and the manifest (`antelopeJs` block with `skills`, plus the interface package's `exports`).
|
|
82
83
|
4. **Wire the basics**: `RegisterModule({ id, title, icon, … })`, one `@RegisterPage()` page,
|
|
83
|
-
|
|
84
|
+
`src/frontend-module.ts` (`frontendModule`, declared as `dmsFrontend`) passed to
|
|
85
|
+
`AddFrontendModule`. Add the `$dms_<name>.*` keys to **every** locale file under
|
|
84
86
|
`frontend-vue/i18n/locales/`.
|
|
85
87
|
5. **Load and verify**: add the module to a consumer project's `antelope.config.ts` (the
|
|
86
88
|
backend's `playground` is the reference consumer) as a `package`/`git`/local-path module,
|
|
@@ -31,7 +31,9 @@ manifest, including the `exports` entries for an interface subpath).
|
|
|
31
31
|
when they provide none (`dms-lang`, `dms-saas`); consumed interfaces are plain `dependencies`
|
|
32
32
|
(no manifest field). Typical dependencies: `@antelopejs/dms` plus
|
|
33
33
|
`@antelopejs/interface-{core,api,api-util,auth,database,database-decorators}`. The frontend is
|
|
34
|
-
attached
|
|
34
|
+
attached by the `AddFrontendModule` call in `construct` and declared as `"dmsFrontend": "dist/frontend-module.js"`
|
|
35
|
+
in `package.json`: `ajs dms dev`, `build` and `start` read that declaration, not the backend, and leave
|
|
36
|
+
out a module without it.
|
|
35
37
|
|
|
36
38
|
**Ship a skill.** Add `skills/<module-name>/SKILL.md` (frontmatter `name`, a trigger-rich
|
|
37
39
|
`description`, `icon`, `category`, `tags`) saying when and how to use the module, list `"skills"` in
|
|
@@ -81,7 +83,10 @@ export function destroy(): void { /* deactivate subscriptions */ }
|
|
|
81
83
|
|
|
82
84
|
`RegisterModule` runs at import time (top level), so the module exists before its pages
|
|
83
85
|
register — it returns the module's root `CategoryInfo`. `AddFrontendModule` is what surfaces the
|
|
84
|
-
module's frontend; without it the module is backend-only.
|
|
86
|
+
module's frontend; without it the module is backend-only. Keep its argument in a pure
|
|
87
|
+
`export function frontendModule(config, { dev })` in `src/frontend-module.ts` (no runtime imports:
|
|
88
|
+
`ajs dms build` loads that file on its own) and call `AddFrontendModule(frontendModule(config, …))`,
|
|
89
|
+
as the official modules do; `dmsFrontend` names its compiled file.
|
|
85
90
|
|
|
86
91
|
`renderer` names the framework (Vue 3), not a loader release. The `@antelopejs/dms-frontend`
|
|
87
92
|
releases the frontend module runs on go in the frontend module's own `package.json`, as
|
|
@@ -37,7 +37,8 @@ infrastructure it needs) and whose own `src/` is the `local` module. It provides
|
|
|
37
37
|
its `package.json` has **no `antelopeJs.implements`** (that field marks a distributable module);
|
|
38
38
|
deps are `@antelopejs/dms` + the interface libs, plus `@antelopejs/dms-frontend` (the
|
|
39
39
|
`ajs dms` CLI) as a dependency — the template keeps it in `dependencies`; a devDependency also works
|
|
40
|
-
since it is only a CLI. The frontend attaches
|
|
40
|
+
since it is only a CLI. The frontend attaches with `AddFrontendModule` and a `dmsFrontend`
|
|
41
|
+
declaration in `package.json` (below), and the DMS
|
|
41
42
|
**auto-registers its base dashboard layer** (shell, login, component vocabulary) — a pages-only
|
|
42
43
|
project needs no `frontend-vue/` of its own.
|
|
43
44
|
|
|
@@ -133,8 +134,13 @@ export function stop(): void {}
|
|
|
133
134
|
export function getConfig(): Config { return globalConfig; }
|
|
134
135
|
```
|
|
135
136
|
|
|
136
|
-
Frontend wiring is the `AddFrontendModule` call
|
|
137
|
-
|
|
137
|
+
Frontend wiring is the `AddFrontendModule` call plus `"dmsFrontend": "dist/frontend-module.js"` in
|
|
138
|
+
the project's `package.json` — no DMS config entry. `ajs dms dev`, `build` and `start` read the project,
|
|
139
|
+
not the backend's registrations, and leave out a frontend that is not declared:
|
|
140
|
+
move the registration into a pure `export function frontendModule()` in `src/frontend-module.ts`
|
|
141
|
+
(no runtime imports), call `AddFrontendModule(frontendModule())` from `start()`, and declare it. A
|
|
142
|
+
**project** attaches the layer from `start()`; a **distributable module** attaches it in `construct`
|
|
143
|
+
(see **dms-module**).
|
|
138
144
|
Full layer authoring: `docs/02.building/08.frontend-layer.md`.
|
|
139
145
|
|
|
140
146
|
## What you register (delegates to the other skills)
|