shutro-erp 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -67
- package/dist/account_portal/.kosh/bindings.d.ts +22 -0
- package/dist/account_portal/.kosh/bindings.js +15 -0
- package/dist/account_portal/.kosh/schema.d.ts +1380 -0
- package/dist/account_portal/index.d.ts +3 -0
- package/dist/account_portal/index.js +73 -0
- package/dist/account_portal/module.d.ts +5983 -0
- package/dist/account_portal/module.js +18 -0
- package/dist/accounting/.kosh/bindings.js +1 -1
- package/dist/accounting/.kosh/schema.d.ts +172 -9
- package/dist/accounting/chart.d.ts +40 -8
- package/dist/accounting/chart.js +124 -30
- package/dist/accounting/closing.d.ts +6 -1
- package/dist/accounting/closing.js +5 -1
- package/dist/accounting/demo.js +6 -5
- package/dist/accounting/index.d.ts +3 -3
- package/dist/accounting/index.js +200 -23
- package/dist/accounting/module.d.ts +3326 -1086
- package/dist/accounting/module.js +408 -66
- package/dist/accounting/payments.d.ts +25 -0
- package/dist/accounting/payments.js +96 -4
- package/dist/accounting/web/RegisterPayment.d.ts +3 -2
- package/dist/accounting/web/RegisterPayment.js +39 -6
- package/dist/accounting/web/SettingsPage.js +5 -1
- package/dist/accounting/web/messages.js +10 -0
- package/dist/assistant/routes.js +1 -1
- package/dist/automation/engine.js +1 -1
- package/dist/automation/web/messages.js +2 -2
- package/dist/cli/apps.d.ts +1 -1
- package/dist/cli/apps.js +14 -14
- package/dist/cli/bin.js +3 -0
- package/dist/cli/index.js +11 -11
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +224 -78
- package/dist/cli/launch.d.ts +16 -0
- package/dist/cli/launch.js +138 -0
- package/dist/cli/project.d.ts +7 -7
- package/dist/cli/project.js +25 -25
- package/dist/cli/prompt.d.ts +35 -0
- package/dist/cli/prompt.js +48 -2
- package/dist/cli/templates.d.ts +14 -6
- package/dist/cli/templates.js +61 -34
- package/dist/cli/tui.d.ts +7 -0
- package/dist/cli/tui.js +254 -0
- package/dist/cli/ui.d.ts +21 -8
- package/dist/cli/ui.js +65 -19
- package/dist/contacts/.kosh/bindings.js +1 -1
- package/dist/contacts/.kosh/schema.d.ts +33 -1
- package/dist/contacts/index.js +24 -1
- package/dist/contacts/module.d.ts +42 -0
- package/dist/contacts/module.js +3 -1
- package/dist/import/convert.d.ts +55 -0
- package/dist/import/convert.js +224 -0
- package/dist/import/csv.d.ts +28 -0
- package/dist/import/csv.js +115 -0
- package/dist/import/index.d.ts +33 -0
- package/dist/import/index.js +60 -0
- package/dist/import/mapping.d.ts +15 -0
- package/dist/import/mapping.js +33 -0
- package/dist/import/match.d.ts +24 -0
- package/dist/import/match.js +60 -0
- package/dist/import/models.d.ts +2 -0
- package/dist/import/models.js +102 -0
- package/dist/import/routes.d.ts +13 -0
- package/dist/import/routes.js +392 -0
- package/dist/import/run.d.ts +131 -0
- package/dist/import/run.js +568 -0
- package/dist/import/service.d.ts +153 -0
- package/dist/import/service.js +367 -0
- package/dist/import/web/ImportButton.d.ts +7 -0
- package/dist/import/web/ImportButton.js +31 -0
- package/dist/import/web/ImportWizard.d.ts +75 -0
- package/dist/import/web/ImportWizard.js +217 -0
- package/dist/import/web/index.d.ts +13 -0
- package/dist/import/web/index.js +18 -0
- package/dist/import/web/messages.d.ts +2 -0
- package/dist/import/web/messages.js +139 -0
- package/dist/import/worker.d.ts +3 -0
- package/dist/import/worker.js +52 -0
- package/dist/import/xlsx.d.ts +20 -0
- package/dist/import/xlsx.js +192 -0
- package/dist/l10n_bd/.kosh/bindings.d.ts +22 -0
- package/dist/l10n_bd/.kosh/bindings.js +15 -0
- package/dist/l10n_bd/.kosh/schema.d.ts +1311 -0
- package/dist/l10n_bd/documents.d.ts +8 -0
- package/dist/l10n_bd/documents.js +183 -0
- package/dist/l10n_bd/index.d.ts +8 -0
- package/dist/l10n_bd/index.js +156 -0
- package/dist/l10n_bd/module.d.ts +5705 -0
- package/dist/l10n_bd/module.js +163 -0
- package/dist/l10n_bd/mushak.d.ts +67 -0
- package/dist/l10n_bd/mushak.js +139 -0
- package/dist/l10n_bd/routes.d.ts +3 -0
- package/dist/l10n_bd/routes.js +205 -0
- package/dist/l10n_bd/web/MushakPage.d.ts +7 -0
- package/dist/l10n_bd/web/MushakPage.js +53 -0
- package/dist/l10n_bd/web/index.d.ts +13 -0
- package/dist/l10n_bd/web/index.js +18 -0
- package/dist/l10n_bd/web/messages.d.ts +2 -0
- package/dist/l10n_bd/web/messages.js +101 -0
- package/dist/l10n_in/.kosh/bindings.d.ts +22 -0
- package/dist/l10n_in/.kosh/bindings.js +15 -0
- package/dist/l10n_in/.kosh/schema.d.ts +1373 -0
- package/dist/l10n_in/documents.d.ts +8 -0
- package/dist/l10n_in/documents.js +114 -0
- package/dist/l10n_in/einvoice.d.ts +253 -0
- package/dist/l10n_in/einvoice.js +485 -0
- package/dist/l10n_in/gstr.d.ts +109 -0
- package/dist/l10n_in/gstr.js +430 -0
- package/dist/l10n_in/index.d.ts +20 -0
- package/dist/l10n_in/index.js +232 -0
- package/dist/l10n_in/module.d.ts +7823 -0
- package/dist/l10n_in/module.js +427 -0
- package/dist/l10n_in/routes-einvoice.d.ts +15 -0
- package/dist/l10n_in/routes-einvoice.js +124 -0
- package/dist/l10n_in/routes.d.ts +17 -0
- package/dist/l10n_in/routes.js +282 -0
- package/dist/l10n_in/web/EInvoicePanel.d.ts +8 -0
- package/dist/l10n_in/web/EInvoicePanel.js +63 -0
- package/dist/l10n_in/web/GstPage.d.ts +7 -0
- package/dist/l10n_in/web/GstPage.js +57 -0
- package/dist/l10n_in/web/GstSettings.d.ts +10 -0
- package/dist/l10n_in/web/GstSettings.js +36 -0
- package/dist/l10n_in/web/index.d.ts +15 -0
- package/dist/l10n_in/web/index.js +22 -0
- package/dist/l10n_in/web/messages.d.ts +2 -0
- package/dist/l10n_in/web/messages.js +166 -0
- package/dist/mail/index.js +2 -2
- package/dist/modules-sdk/demo.d.ts +2 -0
- package/dist/modules-sdk/demo.js +7 -3
- package/dist/modules-sdk/index.d.ts +2 -2
- package/dist/modules-sdk/index.js +1 -1
- package/dist/modules-sdk/loader.js +2 -2
- package/dist/modules-sdk/manifest.d.ts +132 -0
- package/dist/modules-sdk/manifest.js +22 -0
- package/dist/notify/inbox.js +21 -0
- package/dist/notify/messages.js +7 -3
- package/dist/notify/models.d.ts +1 -1
- package/dist/notify/models.js +2 -0
- package/dist/notify/routes.js +1 -1
- package/dist/notify/web/messages.js +5 -1
- package/dist/portal/.kosh/bindings.d.ts +22 -0
- package/dist/portal/.kosh/bindings.js +15 -0
- package/dist/portal/.kosh/schema.d.ts +376 -0
- package/dist/portal/announce.d.ts +16 -0
- package/dist/portal/announce.js +117 -0
- package/dist/portal/documents.d.ts +17 -0
- package/dist/portal/documents.js +61 -0
- package/dist/portal/index.d.ts +20 -0
- package/dist/portal/index.js +65 -0
- package/dist/portal/module.d.ts +1405 -0
- package/dist/portal/module.js +84 -0
- package/dist/portal/routes-documents.d.ts +2 -0
- package/dist/portal/routes-documents.js +391 -0
- package/dist/portal/routes.d.ts +13 -0
- package/dist/portal/routes.js +313 -0
- package/dist/portal/rules.d.ts +11 -0
- package/dist/portal/rules.js +25 -0
- package/dist/portal/web/PortalAccess.d.ts +8 -0
- package/dist/portal/web/PortalAccess.js +66 -0
- package/dist/portal/web/index.d.ts +12 -0
- package/dist/portal/web/index.js +17 -0
- package/dist/portal/web/messages.d.ts +2 -0
- package/dist/portal/web/messages.js +33 -0
- package/dist/product/.kosh/bindings.js +1 -1
- package/dist/product/.kosh/schema.d.ts +42 -3
- package/dist/product/demo.js +6 -5
- package/dist/product/index.d.ts +1 -0
- package/dist/product/index.js +17 -4
- package/dist/product/module.d.ts +346 -14
- package/dist/product/module.js +48 -18
- package/dist/product/taxes.d.ts +59 -0
- package/dist/product/taxes.js +61 -0
- package/dist/project/.kosh/bindings.js +1 -1
- package/dist/project/.kosh/schema.d.ts +33 -1
- package/dist/project/module.d.ts +20 -0
- package/dist/project_portal/.kosh/bindings.d.ts +22 -0
- package/dist/project_portal/.kosh/bindings.js +15 -0
- package/dist/project_portal/.kosh/schema.d.ts +701 -0
- package/dist/project_portal/index.d.ts +9 -0
- package/dist/project_portal/index.js +42 -0
- package/dist/project_portal/module.d.ts +2142 -0
- package/dist/project_portal/module.js +22 -0
- package/dist/purchase/.kosh/bindings.js +1 -1
- package/dist/purchase/.kosh/schema.d.ts +45 -4
- package/dist/purchase/demo.js +3 -3
- package/dist/purchase/index.js +1 -1
- package/dist/purchase/module.d.ts +232 -8
- package/dist/purchase/module.js +51 -9
- package/dist/purchase_account/.kosh/bindings.js +1 -1
- package/dist/purchase_account/.kosh/schema.d.ts +174 -10
- package/dist/purchase_account/module.d.ts +2008 -641
- package/dist/purchase_account/module.js +2 -2
- package/dist/purchase_portal/.kosh/bindings.d.ts +22 -0
- package/dist/purchase_portal/.kosh/bindings.js +15 -0
- package/dist/purchase_portal/.kosh/schema.d.ts +852 -0
- package/dist/purchase_portal/index.d.ts +9 -0
- package/dist/purchase_portal/index.js +53 -0
- package/dist/purchase_portal/module.d.ts +2758 -0
- package/dist/purchase_portal/module.js +18 -0
- package/dist/reporting/documents.js +20 -3
- package/dist/reporting/index.d.ts +1 -0
- package/dist/reporting/index.js +1 -0
- package/dist/reporting/routes-documents.js +9 -2
- package/dist/reporting/words.d.ts +11 -0
- package/dist/reporting/words.js +229 -0
- package/dist/sale_account/.kosh/bindings.js +1 -1
- package/dist/sale_account/.kosh/schema.d.ts +174 -10
- package/dist/sale_account/module.d.ts +2008 -641
- package/dist/sale_account/module.js +3 -3
- package/dist/sale_portal/.kosh/bindings.d.ts +22 -0
- package/dist/sale_portal/.kosh/bindings.js +15 -0
- package/dist/sale_portal/.kosh/schema.d.ts +766 -0
- package/dist/sale_portal/index.d.ts +9 -0
- package/dist/sale_portal/index.js +71 -0
- package/dist/sale_portal/module.d.ts +2718 -0
- package/dist/sale_portal/module.js +19 -0
- package/dist/sale_project/.kosh/bindings.js +1 -1
- package/dist/sale_project/.kosh/schema.d.ts +45 -4
- package/dist/sale_project/module.d.ts +390 -10
- package/dist/sales/.kosh/bindings.js +1 -1
- package/dist/sales/.kosh/schema.d.ts +45 -4
- package/dist/sales/demo.js +3 -3
- package/dist/sales/index.js +67 -1
- package/dist/sales/module.d.ts +232 -8
- package/dist/sales/module.js +51 -12
- package/dist/server/api/client.js +1 -1
- package/dist/server/api/openapi.js +17 -1
- package/dist/server/apps.js +5 -5
- package/dist/server/auth/api-keys.d.ts +13 -0
- package/dist/server/auth/api-keys.js +65 -0
- package/dist/server/auth/base.js +26 -0
- package/dist/server/auth/crypto.js +1 -1
- package/dist/server/auth/scopes.d.ts +12 -0
- package/dist/server/auth/scopes.js +51 -0
- package/dist/server/auth/service.d.ts +59 -1
- package/dist/server/auth/service.js +236 -6
- package/dist/server/demo.d.ts +1 -1
- package/dist/server/demo.js +1 -1
- package/dist/server/env-names.d.ts +2 -0
- package/dist/server/env-names.js +16 -0
- package/dist/server/host.js +5 -1
- package/dist/server/identity.d.ts +7 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +2 -1
- package/dist/server/registry.js +1 -1
- package/dist/server/server.d.ts +5 -0
- package/dist/server/server.js +71 -6
- package/dist/settings/routes.js +1 -1
- package/dist/settings/web/ApiKeysPage.d.ts +8 -0
- package/dist/settings/web/ApiKeysPage.js +70 -0
- package/dist/settings/web/index.d.ts +1 -0
- package/dist/settings/web/index.js +4 -0
- package/dist/settings/web/messages.js +62 -0
- package/dist/storage/routes.js +1 -1
- package/dist/studio/build.js +1 -1
- package/dist/studio/generate.js +4 -4
- package/dist/studio/module.js +5 -5
- package/dist/studio/web/messages.js +2 -2
- package/dist/ui/api.d.ts +2 -0
- package/dist/ui/app.js +27 -4
- package/dist/ui/context.d.ts +3 -1
- package/dist/ui/context.js +2 -2
- package/dist/ui/i18n/messages.js +2 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/lib/format.d.ts +2 -0
- package/dist/ui/lib/format.js +3 -1
- package/dist/ui/pwa/worker.js +2 -2
- package/dist/ui/registry.d.ts +15 -0
- package/dist/ui/registry.js +20 -0
- package/dist/ui/views/FormView.js +19 -6
- package/package.json +52 -4
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* After `shutro-erp init`: the project's dependencies installed with the package manager that ran the installer, the API
|
|
3
|
+
* and the back-office started, each waited for until it answers, and the back-office opened in the default browser.
|
|
4
|
+
* Both keep running in this terminal until Ctrl+C, which stops them together. Their output is kept, and shown when
|
|
5
|
+
* one of them fails to start.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
8
|
+
/** The package manager that ran the CLI (`npx`, `pnpm dlx`, `yarn dlx`, `bunx`), from its user agent; npm otherwise. */
|
|
9
|
+
export function packageManager(env = process.env) {
|
|
10
|
+
const agent = env['npm_config_user_agent'] ?? '';
|
|
11
|
+
for (const pm of ['pnpm', 'yarn', 'bun'])
|
|
12
|
+
if (agent.startsWith(`${pm}/`))
|
|
13
|
+
return pm;
|
|
14
|
+
return 'npm';
|
|
15
|
+
}
|
|
16
|
+
/** The command that opens `url` in the default browser on `platform`. */
|
|
17
|
+
export function browserCommand(url, platform = process.platform) {
|
|
18
|
+
if (platform === 'win32')
|
|
19
|
+
return ['cmd', ['/c', 'start', '""', url]];
|
|
20
|
+
if (platform === 'darwin')
|
|
21
|
+
return ['open', [url]];
|
|
22
|
+
return ['xdg-open', [url]];
|
|
23
|
+
}
|
|
24
|
+
const windows = process.platform === 'win32';
|
|
25
|
+
/** A child process running `pm args` in `cwd`, its output kept (the last 200 lines). */
|
|
26
|
+
function start(pm, args, cwd) {
|
|
27
|
+
const child = spawn(pm, args, {
|
|
28
|
+
cwd,
|
|
29
|
+
env: { ...process.env, FORCE_COLOR: '0' },
|
|
30
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
// npm, pnpm… are .cmd scripts on Windows; elsewhere a process group, stopped as a whole.
|
|
32
|
+
shell: windows,
|
|
33
|
+
detached: !windows,
|
|
34
|
+
});
|
|
35
|
+
const lines = [];
|
|
36
|
+
const keep = (chunk) => {
|
|
37
|
+
lines.push(...chunk
|
|
38
|
+
.toString()
|
|
39
|
+
.split(/\r?\n/)
|
|
40
|
+
.filter((l) => l.trim() !== ''));
|
|
41
|
+
lines.splice(0, Math.max(0, lines.length - 200));
|
|
42
|
+
};
|
|
43
|
+
child.stdout?.on('data', keep);
|
|
44
|
+
child.stderr?.on('data', keep);
|
|
45
|
+
const exited = new Promise((done) => child.on('close', (code) => done(code ?? 1)));
|
|
46
|
+
return { child, lines, exited };
|
|
47
|
+
}
|
|
48
|
+
/** Stops a started process and what it started. */
|
|
49
|
+
function stop(child) {
|
|
50
|
+
if (child.pid === undefined || child.exitCode !== null)
|
|
51
|
+
return;
|
|
52
|
+
try {
|
|
53
|
+
if (windows)
|
|
54
|
+
spawnSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore' });
|
|
55
|
+
else
|
|
56
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// already gone
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Resolves when `url` answers (any HTTP status below 500); rejects when `exited` settles first or on timeout. */
|
|
63
|
+
async function answers(url, exited, seconds) {
|
|
64
|
+
let gone = null;
|
|
65
|
+
void exited.then((code) => {
|
|
66
|
+
gone = code;
|
|
67
|
+
});
|
|
68
|
+
for (let i = 0; i < seconds * 2; i++) {
|
|
69
|
+
if (gone !== null)
|
|
70
|
+
throw new Error(`it stopped (exit code ${gone})`);
|
|
71
|
+
try {
|
|
72
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(1000) });
|
|
73
|
+
if (res.status < 500)
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// not yet
|
|
78
|
+
}
|
|
79
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`no answer at ${url} within ${seconds}s`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Installs, starts and opens the installation; returns when the user stops it (Ctrl+C) or a server stops by itself.
|
|
85
|
+
*/
|
|
86
|
+
export async function launch(o, ui) {
|
|
87
|
+
const pm = packageManager();
|
|
88
|
+
await ui.task(`Installing the dependencies (${pm} install)`, async (task) => {
|
|
89
|
+
task.update('the first run downloads them');
|
|
90
|
+
const run = start(pm, ['install'], o.dir);
|
|
91
|
+
const code = await run.exited;
|
|
92
|
+
if (code !== 0)
|
|
93
|
+
throw new Error(`${pm} install failed:\n${run.lines.slice(-15).join('\n')}`);
|
|
94
|
+
});
|
|
95
|
+
const api = start(pm, ['run', 'start'], o.dir);
|
|
96
|
+
const web = start(pm, ['run', 'web'], o.dir);
|
|
97
|
+
const stopAll = () => {
|
|
98
|
+
stop(api.child);
|
|
99
|
+
stop(web.child);
|
|
100
|
+
};
|
|
101
|
+
process.once('exit', stopAll);
|
|
102
|
+
const failed = (name, run) => (error) => {
|
|
103
|
+
stopAll();
|
|
104
|
+
const tail = run.lines.slice(-15).join('\n');
|
|
105
|
+
throw new Error(`The ${name} did not start: ${error instanceof Error ? error.message : String(error)}${tail === '' ? '' : `\n${tail}`}`);
|
|
106
|
+
};
|
|
107
|
+
const apiUrl = `http://localhost:${o.port}`;
|
|
108
|
+
const webUrl = `http://localhost:${o.webPort}`;
|
|
109
|
+
await ui
|
|
110
|
+
.task(`Starting the API on ${apiUrl}`, () => answers(`${apiUrl}/health/ready`, api.exited, 90))
|
|
111
|
+
.catch(failed('API', api));
|
|
112
|
+
await ui
|
|
113
|
+
.task(`Starting the back-office on ${webUrl}`, () => answers(webUrl, web.exited, 90))
|
|
114
|
+
.catch(failed('back-office', web));
|
|
115
|
+
const [command, args] = browserCommand(webUrl);
|
|
116
|
+
const opened = spawn(command, args, { stdio: 'ignore', detached: true, shell: false });
|
|
117
|
+
opened.on('error', () => ui.info(`Open ${webUrl} in your browser.`));
|
|
118
|
+
opened.unref();
|
|
119
|
+
ui.ok(`Opened ${ui.style('cyan', webUrl)} in your browser · sign in as "${o.login}"`);
|
|
120
|
+
ui.outro(`${ui.style('green', 'Shutro ERP is running')} ${ui.style('gray', `back-office ${webUrl} · API ${apiUrl} · Ctrl+C stops both`)}`);
|
|
121
|
+
// Until Ctrl+C, or until one of them stops by itself (its last lines are shown).
|
|
122
|
+
const which = await Promise.race([
|
|
123
|
+
new Promise((done) => {
|
|
124
|
+
process.once('SIGINT', () => done('stopped'));
|
|
125
|
+
process.once('SIGTERM', () => done('stopped'));
|
|
126
|
+
}),
|
|
127
|
+
api.exited.then(() => 'api'),
|
|
128
|
+
web.exited.then(() => 'web'),
|
|
129
|
+
]);
|
|
130
|
+
stopAll();
|
|
131
|
+
if (which === 'stopped') {
|
|
132
|
+
ui.write(`\n ${ui.style('gray', `Stopped. Start again with: ${pm} start and ${pm} run web`)}\n`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const run = which === 'api' ? api : web;
|
|
136
|
+
ui.fail(`The ${which === 'api' ? 'API' : 'back-office'} stopped:`);
|
|
137
|
+
ui.write(`${run.lines.slice(-15).join('\n')}\n`);
|
|
138
|
+
}
|
package/dist/cli/project.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { KoshModule } from '../modules-sdk/index.js';
|
|
2
2
|
import { type KoshServer } from '../server/index.js';
|
|
3
|
-
/** The
|
|
3
|
+
/** The Shutro ERP modules a file or package exports: `modules`, the default export, or every module export. */
|
|
4
4
|
export declare function modulesOf(specifier: string, cwd: string): Promise<KoshModule[]>;
|
|
5
5
|
/** Opens the installation in `cwd`: `.env`, the modules, a server (not listening). */
|
|
6
6
|
export declare function openProject(cwd: string, modulesFile: string, options?: {
|
|
7
7
|
/** `schema`: installed and disabled modules (what migrations keep); default: the installed ones (what is served). */
|
|
8
8
|
readonly set?: 'served' | 'schema';
|
|
9
9
|
}): Promise<KoshServer>;
|
|
10
|
-
/** What seed data is loaded with: a server's, or an app change's transaction (`
|
|
10
|
+
/** What seed data is loaded with: a server's, or an app change's transaction (`shutro-erp apps`). */
|
|
11
11
|
export interface SeedContext {
|
|
12
12
|
readonly shutro: KoshServer['shutro'];
|
|
13
13
|
readonly driver: Pick<KoshServer['driver'], 'query'>;
|
|
@@ -24,13 +24,13 @@ export declare function migrateProject(server: KoshServer, dir: string, write: (
|
|
|
24
24
|
allowDestructive?: boolean;
|
|
25
25
|
seed?: boolean;
|
|
26
26
|
}): Promise<number>;
|
|
27
|
-
/** `
|
|
27
|
+
/** `shutro-erp migrate [status] …` */
|
|
28
28
|
export declare function runMigrate(argv: readonly string[], write: (t: string) => void, cwd: string): Promise<number>;
|
|
29
|
-
/** `
|
|
29
|
+
/** `shutro-erp seed [module …]` */
|
|
30
30
|
export declare function runSeed(argv: readonly string[], write: (t: string) => void, cwd: string): Promise<number>;
|
|
31
|
-
/** The files of a new module (`
|
|
31
|
+
/** The files of a new module (`shutro-erp module create <name>`). */
|
|
32
32
|
export declare function moduleFiles(name: string): Record<string, string>;
|
|
33
|
-
/** `
|
|
33
|
+
/** `shutro-erp module create <name>`: scaffolds src/modules/<name>/ and lists it in src/modules.ts. */
|
|
34
34
|
export declare function runModule(argv: readonly string[], write: (t: string) => void, cwd: string): Promise<number>;
|
|
35
|
-
/** `
|
|
35
|
+
/** `shutro-erp user create <login> …` and `shutro-erp user password <login>`. */
|
|
36
36
|
export declare function runUser(argv: readonly string[], write: (t: string) => void, cwd: string, prompter?: import('./prompt.js').Prompter): Promise<number>;
|
package/dist/cli/project.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Commands that work on an installation (a folder made by `
|
|
3
|
-
* `
|
|
2
|
+
* Commands that work on an installation (a folder made by `shutro-erp init`): `shutro-erp migrate`, `shutro-erp seed`,
|
|
3
|
+
* `shutro-erp module create`. They read `.env` (DATABASE_URL, storage, ...) and the module list in `src/modules.ts`.
|
|
4
4
|
*
|
|
5
5
|
* - Schema migrations are versioned SQL files in `migrations/`, planned from the modules against the live database
|
|
6
6
|
* (Shutro migrate) and applied in order; each module's data migrations (`migrations` in its Shutro module) run
|
|
@@ -21,7 +21,7 @@ function isModule(value) {
|
|
|
21
21
|
'accessLevels' in value &&
|
|
22
22
|
'name' in value);
|
|
23
23
|
}
|
|
24
|
-
/** The
|
|
24
|
+
/** The Shutro ERP modules a file or package exports: `modules`, the default export, or every module export. */
|
|
25
25
|
export async function modulesOf(specifier, cwd) {
|
|
26
26
|
const target = specifier.startsWith('.') ||
|
|
27
27
|
specifier.startsWith('/') ||
|
|
@@ -39,7 +39,7 @@ export async function modulesOf(specifier, cwd) {
|
|
|
39
39
|
}
|
|
40
40
|
const all = Object.values(exported).filter(isModule);
|
|
41
41
|
if (all.length === 0)
|
|
42
|
-
throw new Error(`${specifier} exports no
|
|
42
|
+
throw new Error(`${specifier} exports no Shutro ERP modules (defineModule from shutro-erp/modules-sdk).`);
|
|
43
43
|
return all;
|
|
44
44
|
}
|
|
45
45
|
/** Opens the installation in `cwd`: `.env`, the modules, a server (not listening). */
|
|
@@ -49,9 +49,9 @@ export async function openProject(cwd, modulesFile, options = {}) {
|
|
|
49
49
|
process.loadEnvFile(envFile);
|
|
50
50
|
const databaseUrl = process.env['DATABASE_URL'];
|
|
51
51
|
if (databaseUrl === undefined || databaseUrl === '')
|
|
52
|
-
throw new Error('DATABASE_URL is not set: run
|
|
52
|
+
throw new Error('DATABASE_URL is not set: run shutro-erp init, or add it to .env.');
|
|
53
53
|
if (!existsSync(resolve(cwd, modulesFile)))
|
|
54
|
-
throw new Error(`${modulesFile} does not exist: run
|
|
54
|
+
throw new Error(`${modulesFile} does not exist: run shutro-erp init, or pass --modules <file>.`);
|
|
55
55
|
process.env['KOSH_SCHEDULES'] = 'off';
|
|
56
56
|
// The module list is the catalogue; the database says which of them are installed (RFC 0008).
|
|
57
57
|
const modules = await installedModules(databaseUrl, await modulesOf(modulesFile, cwd), options.set === 'schema' ? ['installed', 'disabled'] : ['installed']);
|
|
@@ -93,7 +93,7 @@ export async function seedWith(context, write, only) {
|
|
|
93
93
|
: context.modules.filter((m) => only.includes(m.name));
|
|
94
94
|
for (const d of await installDefaults(context.shutro, context.modules, {
|
|
95
95
|
company: companyId,
|
|
96
|
-
reason: '
|
|
96
|
+
reason: 'shutro-erp seed',
|
|
97
97
|
}))
|
|
98
98
|
if (wanted.some((m) => m.name === d.module))
|
|
99
99
|
write(` ${d.module.padEnd(16)} ${d.summary}
|
|
@@ -117,7 +117,7 @@ async function apply(server, dir, write, allowDestructive) {
|
|
|
117
117
|
}
|
|
118
118
|
catch (error) {
|
|
119
119
|
if (error instanceof Error && error.name === 'DestructiveMigrationError') {
|
|
120
|
-
write(`${error.message}\nReview it, then run
|
|
120
|
+
write(`${error.message}\nReview it, then run shutro-erp migrate --allow-destructive.\n`);
|
|
121
121
|
return 1;
|
|
122
122
|
}
|
|
123
123
|
throw error;
|
|
@@ -152,7 +152,7 @@ export async function migrateProject(server, dir, write, options = {}) {
|
|
|
152
152
|
if (options.planOnly === true)
|
|
153
153
|
return 0;
|
|
154
154
|
if (plan.destructive.length > 0 && options.allowDestructive !== true) {
|
|
155
|
-
write('It drops or changes existing data: review the file, then run
|
|
155
|
+
write('It drops or changes existing data: review the file, then run shutro-erp migrate --allow-destructive.\n');
|
|
156
156
|
return 1;
|
|
157
157
|
}
|
|
158
158
|
if (plan.statements.length > 0 || pending.length === 0) {
|
|
@@ -173,11 +173,11 @@ export async function migrateProject(server, dir, write, options = {}) {
|
|
|
173
173
|
await seed(server, write, served !== undefined && served.length > 0 ? served : undefined);
|
|
174
174
|
}
|
|
175
175
|
else
|
|
176
|
-
write('No company yet: seed data waits for
|
|
176
|
+
write('No company yet: seed data waits for shutro-erp init.\n');
|
|
177
177
|
}
|
|
178
178
|
return 0;
|
|
179
179
|
}
|
|
180
|
-
/** `
|
|
180
|
+
/** `shutro-erp migrate [status] …` */
|
|
181
181
|
export async function runMigrate(argv, write, cwd) {
|
|
182
182
|
const { values, positionals } = parseArgs({
|
|
183
183
|
args: [...argv],
|
|
@@ -217,7 +217,7 @@ export async function runMigrate(argv, write, cwd) {
|
|
|
217
217
|
await server.close();
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
/** `
|
|
220
|
+
/** `shutro-erp seed [module …]` */
|
|
221
221
|
export async function runSeed(argv, write, cwd) {
|
|
222
222
|
const { values, positionals } = parseArgs({
|
|
223
223
|
args: [...argv],
|
|
@@ -239,7 +239,7 @@ const title = (name) => name
|
|
|
239
239
|
.split(/[-_]+/)
|
|
240
240
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
241
241
|
.join(' ');
|
|
242
|
-
/** The files of a new module (`
|
|
242
|
+
/** The files of a new module (`shutro-erp module create <name>`). */
|
|
243
243
|
export function moduleFiles(name) {
|
|
244
244
|
const id = camel(name);
|
|
245
245
|
const label = title(name);
|
|
@@ -265,7 +265,7 @@ export const Item = defineModel('${model}', {
|
|
|
265
265
|
});
|
|
266
266
|
`,
|
|
267
267
|
'data.ts': `/**
|
|
268
|
-
* Seed data of the ${label} module: \`
|
|
268
|
+
* Seed data of the ${label} module: \`shutro-erp seed\` (and \`shutro-erp migrate\`) create these records, update them when they
|
|
269
269
|
* change here, and delete records removed from this list. \`noupdate\` records are created once, then left to the users.
|
|
270
270
|
* Refer to other records by external id: orm.ref('res.currency', 'base.currency_bdt').
|
|
271
271
|
*/
|
|
@@ -277,7 +277,7 @@ export const data = [
|
|
|
277
277
|
`,
|
|
278
278
|
'migrations.ts': `/**
|
|
279
279
|
* Data migrations of the ${label} module: each runs once, when an installed module is upgraded past its version
|
|
280
|
-
* (raise \`version\` in index.ts, then \`
|
|
280
|
+
* (raise \`version\` in index.ts, then \`shutro-erp migrate\`). Schema changes need none: \`shutro-erp migrate\` plans them.
|
|
281
281
|
*/
|
|
282
282
|
import type { DataMigration } from 'shutro-erp/orm';
|
|
283
283
|
|
|
@@ -293,7 +293,7 @@ export const migrations: DataMigration[] = [
|
|
|
293
293
|
`,
|
|
294
294
|
'index.ts': `/**
|
|
295
295
|
* The ${label} module: its Shutro models, groups, seed data and data migrations, plus access levels, an app,
|
|
296
|
-
* menus, actions and views. Installed by listing it in src/modules.ts, then \`
|
|
296
|
+
* menus, actions and views. Installed by listing it in src/modules.ts, then \`shutro-erp migrate\`.
|
|
297
297
|
*/
|
|
298
298
|
import { base, baseShutro } from 'shutro-erp';
|
|
299
299
|
import { defineModule } from 'shutro-erp/modules-sdk';
|
|
@@ -340,11 +340,11 @@ export const ${id} = defineModule({
|
|
|
340
340
|
`,
|
|
341
341
|
};
|
|
342
342
|
}
|
|
343
|
-
/** `
|
|
343
|
+
/** `shutro-erp module create <name>`: scaffolds src/modules/<name>/ and lists it in src/modules.ts. */
|
|
344
344
|
export async function runModule(argv, write, cwd) {
|
|
345
345
|
const [action, name] = argv;
|
|
346
346
|
if (action !== 'create' || name === undefined) {
|
|
347
|
-
write('Usage:
|
|
347
|
+
write('Usage: shutro-erp module create <name> (lower-case letters, digits and -, e.g. library)\n');
|
|
348
348
|
return 2;
|
|
349
349
|
}
|
|
350
350
|
if (!/^[a-z][a-z0-9-]{1,40}$/.test(name) ||
|
|
@@ -379,7 +379,7 @@ export async function runModule(argv, write, cwd) {
|
|
|
379
379
|
else
|
|
380
380
|
write(` add ${id} to the modules list in src/modules.ts\n`);
|
|
381
381
|
}
|
|
382
|
-
write(`\nNext:
|
|
382
|
+
write(`\nNext: shutro-erp apps install ${name} (creates its tables and loads its seed data, in one transaction)\n`);
|
|
383
383
|
return 0;
|
|
384
384
|
}
|
|
385
385
|
/** The access levels a role list names: `reporting.user`, `messaging.user`, … (groups of installed modules). */
|
|
@@ -396,7 +396,7 @@ function groupsOf(server, roles) {
|
|
|
396
396
|
throw new Error(`Unknown role(s): ${unknown.join(', ')}. Known: ${[...known].sort().join(', ')}.`);
|
|
397
397
|
return list;
|
|
398
398
|
}
|
|
399
|
-
/** `
|
|
399
|
+
/** `shutro-erp user create <login> …` and `shutro-erp user password <login>`. */
|
|
400
400
|
export async function runUser(argv, write, cwd, prompter) {
|
|
401
401
|
const { values, positionals } = parseArgs({
|
|
402
402
|
args: [...argv],
|
|
@@ -411,8 +411,8 @@ export async function runUser(argv, write, cwd, prompter) {
|
|
|
411
411
|
});
|
|
412
412
|
const [action, login] = positionals;
|
|
413
413
|
if ((action !== 'create' && action !== 'password') || login === undefined) {
|
|
414
|
-
write('Usage:
|
|
415
|
-
'
|
|
414
|
+
write('Usage: shutro-erp user create <login> [--name <name>] [--email <email>] [--roles a.user,b.manager] [--password <pw>]\n' +
|
|
415
|
+
' shutro-erp user password <login> [--password <pw>]\n');
|
|
416
416
|
return 2;
|
|
417
417
|
}
|
|
418
418
|
const { terminalPrompter } = await import('./prompt.js');
|
|
@@ -436,7 +436,7 @@ export async function runUser(argv, write, cwd, prompter) {
|
|
|
436
436
|
params: [],
|
|
437
437
|
});
|
|
438
438
|
if (company === undefined)
|
|
439
|
-
throw new Error('No company yet: run
|
|
439
|
+
throw new Error('No company yet: run shutro-erp init first.');
|
|
440
440
|
const [user] = await server.driver.query({
|
|
441
441
|
text: 'SELECT id::text AS id FROM res_users WHERE login = $1',
|
|
442
442
|
params: [login],
|
|
@@ -444,7 +444,7 @@ export async function runUser(argv, write, cwd, prompter) {
|
|
|
444
444
|
if (action === 'password') {
|
|
445
445
|
if (user === undefined)
|
|
446
446
|
throw new Error(`No user "${login}".`);
|
|
447
|
-
await server.shutro.bootstrap({ reason: `
|
|
447
|
+
await server.shutro.bootstrap({ reason: `shutro-erp user password ${login}` }, (env) => auth.setPassword(env, Number(user.id), password));
|
|
448
448
|
write(`Password of "${login}" changed.\n`);
|
|
449
449
|
return 0;
|
|
450
450
|
}
|
|
@@ -459,7 +459,7 @@ export async function runUser(argv, write, cwd, prompter) {
|
|
|
459
459
|
groups: groupsOf(server, values.roles),
|
|
460
460
|
});
|
|
461
461
|
if (values.email !== undefined)
|
|
462
|
-
await server.shutro.bootstrap({ reason: `
|
|
462
|
+
await server.shutro.bootstrap({ reason: `shutro-erp user create ${login}` }, async (env) => {
|
|
463
463
|
await env.model('res.users')
|
|
464
464
|
.browse([id])
|
|
465
465
|
.write({ email: values.email });
|
package/dist/cli/prompt.d.ts
CHANGED
|
@@ -7,11 +7,46 @@ export interface Question {
|
|
|
7
7
|
/** An error message for an invalid answer (asked again), or `null`. */
|
|
8
8
|
readonly validate?: (value: string) => string | null;
|
|
9
9
|
}
|
|
10
|
+
/** One option of a choice: what is answered, what is shown, an optional hint beside it. */
|
|
11
|
+
export interface Choice {
|
|
12
|
+
readonly value: string;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly hint?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SelectQuestion {
|
|
17
|
+
readonly message: string;
|
|
18
|
+
readonly choices: readonly Choice[];
|
|
19
|
+
/** The value chosen when Enter is pressed at once. */
|
|
20
|
+
readonly default?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface MultiQuestion {
|
|
23
|
+
readonly message: string;
|
|
24
|
+
readonly choices: readonly Choice[];
|
|
25
|
+
/** The values ticked to begin with. */
|
|
26
|
+
readonly defaults?: readonly string[];
|
|
27
|
+
/** At least one. */
|
|
28
|
+
readonly required?: boolean;
|
|
29
|
+
}
|
|
10
30
|
export interface Prompter {
|
|
11
31
|
ask(question: Question): Promise<string>;
|
|
12
32
|
confirm(message: string, fallback: boolean): Promise<boolean>;
|
|
33
|
+
/** One of several (a list chosen with the keyboard); without it, {@link choose} asks a question instead. */
|
|
34
|
+
select?(question: SelectQuestion): Promise<string>;
|
|
35
|
+
/** Several of several; without it, {@link chooseMany} asks for a comma-separated list. */
|
|
36
|
+
multiselect?(question: MultiQuestion): Promise<string[]>;
|
|
13
37
|
close(): void;
|
|
14
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* One of `choices`: the prompter's list when it has one, otherwise the question `fallback` (pipes, scripts, tests),
|
|
41
|
+
* answered with an option's number, value or label.
|
|
42
|
+
*/
|
|
43
|
+
export declare function choose(prompter: Prompter, q: SelectQuestion & {
|
|
44
|
+
readonly fallback: string;
|
|
45
|
+
}): Promise<string>;
|
|
46
|
+
/** Several of `choices`: the prompter's list, or a comma-separated answer to `fallback`. */
|
|
47
|
+
export declare function chooseMany(prompter: Prompter, q: MultiQuestion & {
|
|
48
|
+
readonly fallback: string;
|
|
49
|
+
}): Promise<string[]>;
|
|
15
50
|
/** Thrown when the input ends before every question is answered (piped answers ran out, Ctrl+D). */
|
|
16
51
|
export declare class InputEndedError extends Error {
|
|
17
52
|
constructor(question: string);
|
package/dist/cli/prompt.js
CHANGED
|
@@ -1,8 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Terminal questions for `
|
|
2
|
+
* Terminal questions for `shutro-erp init`, on `node:readline` (no dependency). Answers can be scripted for tests and
|
|
3
3
|
* non-interactive installs (`--yes` with flags): every question goes through {@link Prompter}.
|
|
4
4
|
*/
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
|
+
/**
|
|
7
|
+
* One of `choices`: the prompter's list when it has one, otherwise the question `fallback` (pipes, scripts, tests),
|
|
8
|
+
* answered with an option's number, value or label.
|
|
9
|
+
*/
|
|
10
|
+
export async function choose(prompter, q) {
|
|
11
|
+
if (prompter.select !== undefined)
|
|
12
|
+
return prompter.select(q);
|
|
13
|
+
const find = (answer) => {
|
|
14
|
+
const a = answer.trim().toLowerCase();
|
|
15
|
+
if (/^\d+$/.test(a))
|
|
16
|
+
return q.choices[Number(a) - 1];
|
|
17
|
+
return q.choices.find((c) => c.value.toLowerCase() === a || c.label.toLowerCase() === a);
|
|
18
|
+
};
|
|
19
|
+
const answer = await prompter.ask({
|
|
20
|
+
message: q.fallback,
|
|
21
|
+
...(q.default === undefined ? {} : { default: q.default }),
|
|
22
|
+
validate: (v) => find(v) === undefined ? `One of ${q.choices.map((c) => c.value).join(', ')}` : null,
|
|
23
|
+
});
|
|
24
|
+
return find(answer).value;
|
|
25
|
+
}
|
|
26
|
+
/** Several of `choices`: the prompter's list, or a comma-separated answer to `fallback`. */
|
|
27
|
+
export async function chooseMany(prompter, q) {
|
|
28
|
+
if (prompter.multiselect !== undefined)
|
|
29
|
+
return prompter.multiselect(q);
|
|
30
|
+
const known = new Set(q.choices.map((c) => c.value));
|
|
31
|
+
const answer = await prompter.ask({
|
|
32
|
+
message: q.fallback,
|
|
33
|
+
default: (q.defaults ?? []).join(','),
|
|
34
|
+
validate: (v) => {
|
|
35
|
+
const items = v
|
|
36
|
+
.split(',')
|
|
37
|
+
.map((x) => x.trim())
|
|
38
|
+
.filter((x) => x !== '');
|
|
39
|
+
const unknown = items.filter((x) => !known.has(x));
|
|
40
|
+
if (unknown.length > 0)
|
|
41
|
+
return `Unknown: ${unknown.join(', ')}`;
|
|
42
|
+
return q.required === true && items.length === 0 ? 'Choose at least one.' : null;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return [
|
|
46
|
+
...new Set(answer
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((x) => x.trim())
|
|
49
|
+
.filter((x) => x !== '')),
|
|
50
|
+
];
|
|
51
|
+
}
|
|
6
52
|
/** Thrown when the input ends before every question is answered (piped answers ran out, Ctrl+D). */
|
|
7
53
|
export class InputEndedError extends Error {
|
|
8
54
|
constructor(question) {
|
|
@@ -104,7 +150,7 @@ export function defaultsPrompter() {
|
|
|
104
150
|
return {
|
|
105
151
|
async ask(q) {
|
|
106
152
|
if (q.default === undefined)
|
|
107
|
-
throw new Error(`"${q.message}" has no default: pass it as a flag (
|
|
153
|
+
throw new Error(`"${q.message}" has no default: pass it as a flag (shutro-erp init --help).`);
|
|
108
154
|
const problem = q.validate?.(q.default) ?? null;
|
|
109
155
|
if (problem !== null)
|
|
110
156
|
throw new Error(`${q.message}: ${problem}`);
|
package/dist/cli/templates.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The starter project `
|
|
2
|
+
* The starter project `shutro-erp init` writes: an API server and the back-office app, both importing `shutro-erp`.
|
|
3
3
|
* Server files run on Node.js directly (type stripping); the back-office is a Vite app proxying `/api` to the server.
|
|
4
4
|
*/
|
|
5
|
-
export type AppName = 'permissions' | 'reporting' | 'messaging' | 'contacts' | 'product' | 'sales' | 'purchase' | 'accounting' | 'sale_account' | 'purchase_account' | 'project' | 'sale_project';
|
|
5
|
+
export type AppName = 'permissions' | 'reporting' | 'messaging' | 'contacts' | 'product' | 'sales' | 'purchase' | 'accounting' | 'sale_account' | 'purchase_account' | 'project' | 'sale_project' | 'l10n_bd' | 'l10n_in';
|
|
6
6
|
export interface AppSpec {
|
|
7
7
|
readonly label: string;
|
|
8
8
|
/** Shown next to the app in the installer (where the terminal shows pictographs). */
|
|
@@ -13,17 +13,21 @@ export interface AppSpec {
|
|
|
13
13
|
readonly web?: string;
|
|
14
14
|
/** A bridge (RFC 0008 §5.1): not offered in the installer; proposed when the apps it connects are installed. */
|
|
15
15
|
readonly bridge?: readonly AppName[];
|
|
16
|
+
/** A localization (RFC 0016): installed, with its bridged apps, for a company in this country (ISO 3166). */
|
|
17
|
+
readonly country?: string;
|
|
16
18
|
}
|
|
17
|
-
/** The apps `
|
|
19
|
+
/** The apps `shutro-erp init` can install (base and Settings are always there), in the order the installer lists them. */
|
|
18
20
|
export declare const APPS: Readonly<Record<AppName, AppSpec>>;
|
|
19
21
|
export interface StarterOptions {
|
|
20
22
|
readonly name: string;
|
|
21
23
|
readonly apps: readonly AppName[];
|
|
22
24
|
readonly port: number;
|
|
25
|
+
/** The back-office's port in development (default 5173). */
|
|
26
|
+
readonly webPort?: number;
|
|
23
27
|
readonly koshVersion: string;
|
|
24
28
|
/** The database name of the Docker PostgreSQL service (compose.yaml). */
|
|
25
29
|
readonly dockerDb?: string;
|
|
26
|
-
/** Attachments in a folder (
|
|
30
|
+
/** Attachments in a folder (SHUTRO_ERP_STORAGE_DIR) rather than S3. */
|
|
27
31
|
readonly fileStorage?: boolean;
|
|
28
32
|
}
|
|
29
33
|
/** The project's package.json (merged into an existing one: missing scripts and dependencies are added). */
|
|
@@ -52,5 +56,9 @@ export declare function tsconfig(): string;
|
|
|
52
56
|
export declare function readme(options: StarterOptions): string;
|
|
53
57
|
/** The apps the installer offers (every app but the bridges, which come by themselves). */
|
|
54
58
|
export declare const SELECTABLE_APPS: readonly AppName[];
|
|
55
|
-
/** The bridges whose every connected app is in `apps
|
|
56
|
-
export declare function bridgesFor(apps: readonly AppName[]): AppName[];
|
|
59
|
+
/** The bridges whose every connected app is in `apps`, and the localization of `country` among them. */
|
|
60
|
+
export declare function bridgesFor(apps: readonly AppName[], country?: string): AppName[];
|
|
61
|
+
/** The names of the countries with a localization (the installer's list). */
|
|
62
|
+
export declare const COUNTRY_NAMES: Readonly<Record<string, string>>;
|
|
63
|
+
/** The countries with a localization, for the installer's question. */
|
|
64
|
+
export declare const LOCALIZED_COUNTRIES: readonly string[];
|