orionplace-nuxt 0.1.1 → 0.1.3
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 +64 -7
- package/bin/orionplace-nuxt.mjs +32 -13
- package/package.json +3 -2
- package/src/module.mjs +2 -0
- package/src/project-readme.mjs +78 -33
- package/src/setup.mjs +52 -10
- package/src/update.mjs +36 -0
- package/templates/app/app.vue +90 -0
- package/templates/app/composables/useOrionplaceAppLoad.ts +51 -0
- package/templates/app/interfaces/orionplace/apiBase/TMethod.ts +1 -0
- package/templates/app/interfaces/orionplace/appBase/IAppLoad.ts +15 -0
- package/templates/app/interfaces/orionplace/appBase/IAppLoadCommon.ts +54 -0
- package/templates/app/interfaces/orionplace/appBase/IApploadUserProfile.ts +13 -0
- package/templates/app/interfaces/orionplace/appBase/IForm.ts +21 -0
- package/templates/app/interfaces/orionplace/appBase/ISEO.ts +21 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonBanner.ts +11 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonContact.ts +8 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonCurrency.ts +15 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonEvents.ts +18 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonLocale.ts +14 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonLogos.ts +6 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonPath.ts +22 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonSEO.ts +13 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonSocial.ts +15 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonTariff.ts +7 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonUnit.ts +12 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonVariables.ts +7 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonVisualSettings.ts +11 -0
- package/templates/app/interfaces/orionplace/appBase/appLoadCommon/IAppLoadCommonWalletValue.ts +3 -0
- package/templates/app/interfaces/orionplace/category/ICategory.ts +40 -0
- package/templates/app/interfaces/orionplace/domains/IDomain.ts +13 -0
- package/templates/app/interfaces/orionplace/index.ts +29 -0
- package/templates/app/interfaces/orionplace/products/IProduct.ts +235 -0
- package/templates/app/interfaces/orionplace/shared/IGeoCoordinates.ts +4 -0
- package/templates/app/interfaces/orionplace/shared/ITransformValue.ts +3 -0
- package/templates/app/interfaces/orionplace/subscribeOptions/ISubscribeOption.ts +9 -0
- package/templates/app/interfaces/orionplace/subscribeTariff/ISubscribeTariff.ts +46 -0
- package/templates/app/interfaces/orionplace/subscribes/ISubscribe.ts +28 -0
- package/templates/app/interfaces/orionplace/transport.ts +30 -0
- package/templates/app/utils/orionplaceAppLoad.ts +33 -0
package/README.md
CHANGED
|
@@ -62,12 +62,13 @@ to inspect planned changes without writing them.
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
npx orionplace-nuxt init [directory]
|
|
65
|
+
npx orionplace-nuxt update
|
|
65
66
|
npx orionplace-nuxt set backend your-project.orionplace.app
|
|
66
67
|
npx orionplace-nuxt add frontend shop.example.com
|
|
67
68
|
npx orionplace-nuxt add frontend www.shop.example.com
|
|
68
69
|
npx orionplace-nuxt remove frontend www.shop.example.com
|
|
69
|
-
npx orionplace-nuxt set
|
|
70
|
-
npx orionplace-nuxt
|
|
70
|
+
npx orionplace-nuxt set mimic shop.example.com
|
|
71
|
+
npx orionplace-nuxt help
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
Either domain command can run first. Both settings are needed for a usable mapping.
|
|
@@ -77,6 +78,9 @@ After setup, these npm script aliases are also available:
|
|
|
77
78
|
npm run orionplace:backend -- your-project.orionplace.app
|
|
78
79
|
npm run orionplace:add-frontend -- shop.example.com
|
|
79
80
|
npm run orionplace:remove-frontend -- www.shop.example.com
|
|
81
|
+
npm run orionplace:mimic -- shop.example.com
|
|
82
|
+
npm run orionplace:help
|
|
83
|
+
npm run orionplace:update
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
`npm set` belongs to npm's own configuration system; use the commands above.
|
|
@@ -100,7 +104,7 @@ change your account, create DNS records, configure TLS, or deploy a VPS.
|
|
|
100
104
|
The module executes the generated local runtime files. You can read and customize
|
|
101
105
|
them. Later setup runs preserve edited runtime files and the project README,
|
|
102
106
|
reporting which files were preserved. Unchanged generated files can be refreshed
|
|
103
|
-
|
|
107
|
+
with `npx orionplace-nuxt update`. A manually edited domain file is
|
|
104
108
|
never silently overwritten. Keep the hash manifest in version control.
|
|
105
109
|
|
|
106
110
|
## Multiple frontend domains
|
|
@@ -109,9 +113,14 @@ Each frontend gets its own `sitemaps/<domain>.cfg`. `add frontend` preserves oth
|
|
|
109
113
|
domains and can be repeated safely. `set backend` updates every CLI-managed mapping
|
|
110
114
|
to the new personal API; it refuses to overwrite a manually edited mapping.
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
A new project starts with `MIMIC_FRONT_DOMAIN=""` in `.env`. Setting a backend
|
|
117
|
+
alone leaves it empty. The first added frontend becomes MIMIC; further additions
|
|
118
|
+
preserve that choice. `set mimic <domain>` selects an already-added frontend and
|
|
119
|
+
updates MIMIC without changing any domain mappings. Unknown domains are rejected;
|
|
120
|
+
add them with `add frontend` first.
|
|
121
|
+
|
|
122
|
+
`help`, `--help`, and `-h` display commands without writing files. The legacy
|
|
123
|
+
`set frontend <domain>` shortcut remains supported: it adds and selects a domain.
|
|
115
124
|
|
|
116
125
|
`remove frontend <domain>` removes that domain from the list and deletes only its
|
|
117
126
|
unchanged generated `.cfg`. Removing the MIMIC domain selects the first remaining
|
|
@@ -167,4 +176,52 @@ npm pack --dry-run
|
|
|
167
176
|
```
|
|
168
177
|
|
|
169
178
|
The published tarball includes only the CLI, module, runtime source, README,
|
|
170
|
-
license, and package metadata.
|
|
179
|
+
license, starter templates, and package metadata.
|
|
180
|
+
|
|
181
|
+
## AppLoad starter
|
|
182
|
+
|
|
183
|
+
The CLI generates an AppLoad debug `app/app.vue`, an SSR-aware
|
|
184
|
+
`useOrionplaceAppLoad()` composable, validation helpers and the full nested
|
|
185
|
+
TypeScript contract under `app/interfaces/orionplace/`. The first page loads
|
|
186
|
+
`POST /api/appload`, shows request state and section counts, and supports reload.
|
|
187
|
+
It preserves custom apps and edited generated files. Generated instructions in
|
|
188
|
+
`orionplace/README.md` explain how to integrate the composable in an existing app.
|
|
189
|
+
|
|
190
|
+
## Update an existing project
|
|
191
|
+
|
|
192
|
+
After the initial setup, use one command to get the latest package and components:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npx orionplace-nuxt update
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
It runs the latest CLI, adds new template files, refreshes unchanged generated
|
|
199
|
+
files, updates the package dependency and runs `npm install` to synchronize the
|
|
200
|
+
lockfile and installed dependencies. Edited files are preserved and listed in the
|
|
201
|
+
output. Review their new versions under `node_modules/orionplace-nuxt/templates`
|
|
202
|
+
or `runtime` and merge your changes manually when needed. Removed upstream files
|
|
203
|
+
are retained locally; the updater does not delete customer source.
|
|
204
|
+
|
|
205
|
+
Domain mappings, domain settings and `.env` remain unchanged. The command does
|
|
206
|
+
not deploy your application: restart development, or rebuild and redeploy the
|
|
207
|
+
production frontend after updating.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx orionplace-nuxt update --dry-run
|
|
211
|
+
npm run orionplace:update
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Dry-run downloads the latest CLI into npm's cache to inspect its templates but
|
|
215
|
+
leaves project files and dependencies unchanged. `update --local` applies the
|
|
216
|
+
version of the CLI being executed instead of fetching the latest CLI; it still
|
|
217
|
+
installs project dependencies. Use this only when intentionally selecting a version.
|
|
218
|
+
|
|
219
|
+
For a project on 0.1.2 or older, bootstrap the new command once:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
npx --yes orionplace-nuxt@latest update
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Updates require the existing `orionplace/settings.json` and `.generated.json`.
|
|
226
|
+
Conflicting untracked files stop the source update before any writes. If dependency
|
|
227
|
+
installation fails after source generation, run `npm install` and retry `update`.
|
package/bin/orionplace-nuxt.mjs
CHANGED
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setupProject } from '../src/setup.mjs';
|
|
3
|
+
import { updateProject } from '../src/update.mjs';
|
|
3
4
|
import { readFile } from 'node:fs/promises';
|
|
4
5
|
import { resolve } from 'node:path';
|
|
5
6
|
import { createInterface } from 'node:readline/promises';
|
|
6
7
|
|
|
7
8
|
const help = `Orionplace Nuxt setup
|
|
8
9
|
|
|
10
|
+
npx orionplace-nuxt help
|
|
9
11
|
npx orionplace-nuxt init [directory]
|
|
12
|
+
npx orionplace-nuxt update
|
|
10
13
|
npx orionplace-nuxt set backend <personal-backend.orionplace.app>
|
|
11
14
|
npx orionplace-nuxt add frontend <shop.example.com>
|
|
12
15
|
npx orionplace-nuxt remove frontend <shop.example.com>
|
|
16
|
+
npx orionplace-nuxt set mimic <existing-frontend.example.com>
|
|
13
17
|
npx orionplace-nuxt set frontend <shop.example.com>
|
|
14
18
|
|
|
15
19
|
Options: --cwd <directory> --dry-run --help --version
|
|
20
|
+
Update option: --local (apply this CLI version instead of fetching the latest release)
|
|
16
21
|
|
|
22
|
+
Update downloads the latest release, refreshes unedited source, and installs dependencies.
|
|
23
|
+
It preserves edited source, domain mappings and MIMIC. --dry-run leaves project files unchanged.
|
|
17
24
|
Set backend or add frontend also initializes the integration; either can come first.
|
|
18
|
-
|
|
25
|
+
MIMIC starts empty. The first added frontend sets it; later additions preserve it.
|
|
26
|
+
Set mimic selects an existing frontend without changing any domain mappings.
|
|
27
|
+
Set frontend is the legacy shortcut to add and select a frontend.
|
|
19
28
|
Set backend updates every managed frontend mapping. Remove frontend deletes only that mapping.
|
|
20
29
|
After creating a new project, run npm install and npm run dev in that directory.
|
|
21
30
|
Documentation and editable server source are generated in orionplace/.`;
|
|
22
31
|
try {
|
|
23
32
|
const argv = process.argv.slice(2);
|
|
24
|
-
if (!argv.length || argv.includes('--help') || argv.includes('-h')) {
|
|
33
|
+
if (!argv.length || argv[0] === 'help' || argv.includes('--help') || argv.includes('-h')) {
|
|
25
34
|
console.log(help);
|
|
26
35
|
} else if (argv.includes('--version')) {
|
|
27
36
|
console.log(JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')).version);
|
|
@@ -29,8 +38,10 @@ try {
|
|
|
29
38
|
const positional = [];
|
|
30
39
|
let cwd = process.cwd();
|
|
31
40
|
let dryRun = false;
|
|
41
|
+
let local = false;
|
|
32
42
|
for (let index = 0; index < argv.length; index++) {
|
|
33
43
|
if (argv[index] === '--dry-run') dryRun = true;
|
|
44
|
+
else if (argv[index] === '--local') local = true;
|
|
34
45
|
else if (argv[index] === '--cwd') {
|
|
35
46
|
if (!argv[index + 1]) throw new Error('--cwd needs a directory');
|
|
36
47
|
cwd = resolve(argv[++index]);
|
|
@@ -38,29 +49,37 @@ try {
|
|
|
38
49
|
else positional.push(argv[index]);
|
|
39
50
|
}
|
|
40
51
|
const [command, target] = positional;
|
|
52
|
+
if (local && command !== 'update') throw new Error('--local is only supported by update.');
|
|
41
53
|
let result;
|
|
42
54
|
if (command === 'init' && positional.length <= 2) {
|
|
43
55
|
result = await setupProject({ cwd: target ? resolve(cwd, target) : cwd, dryRun });
|
|
44
|
-
} else if (
|
|
56
|
+
} else if (command === 'update' && positional.length === 1) {
|
|
57
|
+
result = await updateProject({ cwd, dryRun, local });
|
|
58
|
+
} else if (((command === 'set' && ['backend', 'frontend', 'mimic'].includes(target)) ||
|
|
45
59
|
(['add', 'remove'].includes(command) && target === 'frontend')) && positional.length <= 3) {
|
|
46
60
|
let value = positional[2];
|
|
47
61
|
if (!value && process.stdin.isTTY) {
|
|
48
62
|
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
49
|
-
try { value = await prompt.question(`${target === 'backend' ? 'Personal backend API' : 'Public frontend domain'}: `); }
|
|
63
|
+
try { value = await prompt.question(`${target === 'backend' ? 'Personal backend API' : target === 'mimic' ? 'Existing frontend to use for MIMIC' : 'Public frontend domain'}: `); }
|
|
50
64
|
finally { prompt.close(); }
|
|
51
65
|
}
|
|
52
66
|
if (!value) throw new Error(`Provide the ${target} domain. See --help.`);
|
|
53
67
|
result = await setupProject({ cwd, action: command, field: target, value, dryRun });
|
|
54
68
|
} else throw new Error('Unknown command. See --help.');
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
if (result) {
|
|
70
|
+
console.log(`${dryRun ? 'Planned' : 'Updated'} ${result.root}`);
|
|
71
|
+
for (const path of result.changed) console.log(` ${result.removed.includes(path) ? 'Remove: ' : ''}${path}`);
|
|
72
|
+
for (const path of result.preserved) console.log(` Preserved your edits: ${path}`);
|
|
73
|
+
console.log(result.settings.backend && result.settings.frontend
|
|
74
|
+
? `Ready: ${result.settings.frontends.join(', ')} -> ${result.settings.backend}\nLocal MIMIC: ${result.settings.frontend}`
|
|
75
|
+
: `Next: ${result.settings.backend ? 'add frontend' : 'set backend'} to complete the domain mapping.`);
|
|
76
|
+
if (result.changed.some(path => path.endsWith('.cfg') || path === '.env')) console.log('Restart a running development server to refresh allowed hosts and MIMIC. Reload your production environment when changing MIMIC.');
|
|
77
|
+
if (result.isNewNuxt) console.log('Run npm install in the project, then npm run dev.');
|
|
78
|
+
console.log('Project documentation: orionplace/README.md');
|
|
79
|
+
if (command === 'update') console.log(dryRun
|
|
80
|
+
? 'Dependency installation would run after applying these changes. Project files were not changed.'
|
|
81
|
+
: 'Update complete. Restart development, or rebuild and redeploy your production frontend.');
|
|
82
|
+
}
|
|
64
83
|
}
|
|
65
84
|
} catch (error) {
|
|
66
85
|
console.error(`Orionplace: ${error.message}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orionplace-nuxt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Nuxt integration and setup CLI for your Orionplace personal marketplace backend.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,11 +31,12 @@
|
|
|
31
31
|
"bin",
|
|
32
32
|
"src",
|
|
33
33
|
"runtime",
|
|
34
|
+
"templates",
|
|
34
35
|
"README.md",
|
|
35
36
|
"LICENSE"
|
|
36
37
|
],
|
|
37
38
|
"scripts": {
|
|
38
|
-
"test": "node --test test/*.test.mjs"
|
|
39
|
+
"test": "node --experimental-strip-types --test test/*.test.mjs"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"nuxt": "^4.0.0"
|
package/src/module.mjs
CHANGED
|
@@ -29,6 +29,8 @@ export default defineNuxtModule({
|
|
|
29
29
|
// Do not inherit page caching for authenticated API requests.
|
|
30
30
|
routes['/api/**'] = { ...routes['/api/**'], cache: false, swr: false,
|
|
31
31
|
headers: { ...routes['/api/**']?.headers, 'cache-control': 'no-store' } };
|
|
32
|
+
// MIMIC defaults to empty. The CLI sets .env on the first "add frontend"
|
|
33
|
+
// or an explicit "set mimic"; never infer it from the domain-file directory.
|
|
32
34
|
// Values stay private to the server. MIMIC_FRONT_DOMAIN is also read at runtime.
|
|
33
35
|
nuxt.options.runtimeConfig.mimicFrontDomain ??= '';
|
|
34
36
|
nuxt.options.runtimeConfig.mimicFrontDomain = process.env.MIMIC_FRONT_DOMAIN
|
package/src/project-readme.mjs
CHANGED
|
@@ -10,7 +10,7 @@ You can change that backend domain in your account, then update this project.
|
|
|
10
10
|
## Current setup
|
|
11
11
|
|
|
12
12
|
- Frontend domains: ${settings.frontends?.length ? settings.frontends.map(domain => '\`' + domain + '\`').join(', ') : 'not configured yet'}
|
|
13
|
-
- Local MIMIC domain: \`${settings.frontend || '
|
|
13
|
+
- Local MIMIC domain: \`${settings.frontend || 'empty'}\`
|
|
14
14
|
- Personal API: \`${settings.backend || 'not configured yet'}\`
|
|
15
15
|
|
|
16
16
|
Set or change these values from the project root:
|
|
@@ -26,9 +26,19 @@ Set the backend or add a frontend first; either order works. Each frontend has i
|
|
|
26
26
|
own \`sitemaps/<domain>.cfg\` mapping to your personal API. Adding a domain again
|
|
27
27
|
is safe. Changing the backend updates every CLI-managed frontend mapping.
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
MIMIC is empty in a new project: \`MIMIC_FRONT_DOMAIN=""\`. Setting the backend
|
|
30
|
+
alone leaves it empty. The first added frontend sets MIMIC; adding more domains
|
|
31
|
+
preserves that choice.
|
|
32
|
+
|
|
33
|
+
Use \`npx orionplace-nuxt set mimic <domain>\` to select an already-added frontend
|
|
34
|
+
for localhost. It changes MIMIC without editing domain mappings. Add an unknown
|
|
35
|
+
domain with \`add frontend\` first. The legacy \`set frontend\` shortcut still
|
|
36
|
+
adds and selects a frontend.
|
|
37
|
+
|
|
38
|
+
\`\`\`bash
|
|
39
|
+
npx orionplace-nuxt help
|
|
40
|
+
npx orionplace-nuxt set mimic ${frontend}
|
|
41
|
+
\`\`\`
|
|
32
42
|
|
|
33
43
|
Removing a frontend deletes only its unchanged generated \`.cfg\` file. If it was
|
|
34
44
|
the selected MIMIC domain, the first remaining domain takes its place. Removing
|
|
@@ -88,53 +98,88 @@ In another terminal:
|
|
|
88
98
|
\`\`\`bash
|
|
89
99
|
curl -i http://localhost:3000/api/appload \\
|
|
90
100
|
-H 'Content-Type: application/json' \\
|
|
91
|
-
--data '{"ignore_cache":true,"detalize_mode":"
|
|
101
|
+
--data '{"ignore_cache":true,"detalize_mode":"light"}'
|
|
92
102
|
\`\`\`
|
|
93
103
|
|
|
94
104
|
Check both the HTTP status and \`form_errors\`. Successful initialization returns
|
|
95
105
|
\`page_data\`; HTTP 200 by itself is insufficient. Documentation and public requests
|
|
96
106
|
do not require a login prompt. Protected actions still require backend permission.
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
## AppLoad initialization
|
|
109
|
+
|
|
110
|
+
The generated \`app/app.vue\` calls \`useOrionplaceAppLoad()\` and displays loading,
|
|
111
|
+
success or failure, HTTP status, session state and counts of returned sections.
|
|
112
|
+
Use **Reload AppLoad** to issue a fresh request. A failed request never opens a
|
|
113
|
+
login modal. Once the connection works, replace this debug page with your storefront.
|
|
114
|
+
|
|
115
|
+
| Project file | Purpose |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| \`app/composables/useOrionplaceAppLoad.ts\` | SSR-aware initialization and manual reload. |
|
|
118
|
+
| \`app/utils/orionplaceAppLoad.ts\` | HTTP, form-error and bootstrap-envelope checks. |
|
|
119
|
+
| \`app/interfaces/orionplace/index.ts\` | All AppLoad interfaces and nested model exports. |
|
|
120
|
+
| \`app/interfaces/orionplace/transport.ts\` | Request, response, result and error contracts. |
|
|
121
|
+
|
|
122
|
+
The interfaces cover initialization settings, locales, currencies, units, catalog,
|
|
123
|
+
products, banners, events, tariffs and subscriptions, API discovery and forms,
|
|
124
|
+
and the optional signed-in profile. Plugin-defined JSON remains explicitly typed
|
|
125
|
+
as JSON; interfaces are not a full runtime schema validator.
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
The server uses the current request's domain and session. Its result is reused
|
|
128
|
+
during browser hydration, and new response cookies are relayed to the browser.
|
|
129
|
+
The page response uses \`private, no-store\`; do not put personalized pages behind
|
|
130
|
+
shared HTML caches. Reloading replaces the current session's discovery data.
|
|
131
|
+
|
|
132
|
+
Requests use \`detalize_mode: "light"\` and \`ignore_cache: true\`. Tenant settings
|
|
133
|
+
can override these options, and individual initialization sections have separate
|
|
134
|
+
backend caches. Full endpoint descriptions are therefore optional.
|
|
135
|
+
|
|
136
|
+
If your project already has a custom \`app/app.vue\`, the CLI preserves it.
|
|
137
|
+
Integrate the generated composable explicitly:
|
|
113
138
|
|
|
114
139
|
\`\`\`vue
|
|
115
|
-
<script setup>
|
|
116
|
-
|
|
117
|
-
const { data, error } = await
|
|
118
|
-
|
|
119
|
-
method: 'POST',
|
|
120
|
-
body: { ignore_cache: true, detalize_mode: 'full' },
|
|
121
|
-
}),
|
|
122
|
-
);
|
|
140
|
+
<script setup lang="ts">
|
|
141
|
+
import { useOrionplaceAppLoad } from './composables/useOrionplaceAppLoad';
|
|
142
|
+
const { data, status, error, refresh } = await useOrionplaceAppLoad();
|
|
143
|
+
// data.value?.response.page_data contains the typed initialization and discovery.
|
|
123
144
|
</script>
|
|
124
145
|
|
|
125
146
|
<template>
|
|
126
147
|
<main>
|
|
127
|
-
<p
|
|
128
|
-
<
|
|
148
|
+
<p role="status">AppLoad: {{ status }}</p>
|
|
149
|
+
<p v-if="error" role="alert">Marketplace initialization failed.</p>
|
|
150
|
+
<button type="button" :disabled="status === 'pending'" @click="refresh()">Reload AppLoad</button>
|
|
151
|
+
<p v-if="data">HTTP {{ data.httpStatus }}</p>
|
|
129
152
|
</main>
|
|
130
153
|
</template>
|
|
131
154
|
\`\`\`
|
|
132
155
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
Call the composable once in your app root and pass the data to your components.
|
|
157
|
+
The generated debug view displays section summaries; it does not render profile
|
|
158
|
+
fields, keys or backend exception stacks. The typed response is still available
|
|
159
|
+
to your application, so avoid logging or publishing it indiscriminately.
|
|
160
|
+
|
|
161
|
+
CLI reruns preserve edited generated source. Review updated package templates
|
|
162
|
+
manually when you have customized a file. Existing custom apps are not replaced.
|
|
163
|
+
|
|
164
|
+
## Update the package and components
|
|
165
|
+
|
|
166
|
+
Run this from your project root whenever you want the latest release:
|
|
167
|
+
|
|
168
|
+
\`\`\`bash
|
|
169
|
+
npx orionplace-nuxt update
|
|
170
|
+
\`\`\`
|
|
137
171
|
|
|
172
|
+
The command fetches the latest CLI, adds new template files, refreshes unchanged
|
|
173
|
+
generated source, updates the package dependency, and runs \`npm install\`.
|
|
174
|
+
It preserves edited files and lists them for manual review. Find their incoming
|
|
175
|
+
versions under \`node_modules/orionplace-nuxt/templates\` or \`runtime\`.
|
|
176
|
+
Domain mappings, domain settings and \`.env\` are preserved. Upstream file removals
|
|
177
|
+
do not delete local source. Restart development or rebuild and redeploy afterward.
|
|
178
|
+
|
|
179
|
+
Use \`npx orionplace-nuxt update --dry-run\` to review changes without modifying
|
|
180
|
+
project files or installed dependencies; downloading the CLI may update npm's
|
|
181
|
+
cache. The alias \`npm run orionplace:update\` performs the same normal update.
|
|
182
|
+
If installation fails after source generation, run \`npm install\` and retry.
|
|
138
183
|
|
|
139
184
|
## Deploy on your VPS
|
|
140
185
|
|
package/src/setup.mjs
CHANGED
|
@@ -57,17 +57,21 @@ function updateEnvironment(source, domain) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export async function setupProject({ cwd = process.cwd(), action = 'set', field, value, dryRun = false } = {}) {
|
|
60
|
-
if (!['set', 'add', 'remove'].includes(action) ||
|
|
61
|
-
|
|
60
|
+
if (!['set', 'add', 'remove', 'update'].includes(action) ||
|
|
61
|
+
(['add', 'remove'].includes(action) && field !== 'frontend') || (action === 'update' && field)) {
|
|
62
|
+
throw new Error('Use set backend, add frontend, remove frontend, set mimic, or set frontend.');
|
|
62
63
|
}
|
|
63
64
|
const root = resolve(cwd);
|
|
64
|
-
const setting = field === 'backend' ? backendUrl(value) :
|
|
65
|
-
if (field && !setting) throw new Error('Choose backend or
|
|
65
|
+
const setting = field === 'backend' ? backendUrl(value) : ['frontend', 'mimic'].includes(field) ? frontendDomain(value) : null;
|
|
66
|
+
if (field && !setting) throw new Error('Choose backend, frontend, or mimic.');
|
|
66
67
|
const names = ['package.json', 'nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs', 'orionplace/settings.json', 'orionplace/.generated.json', '.env', '.gitignore'];
|
|
67
68
|
const current = {};
|
|
68
69
|
for (const name of names) current[name] = await read(await safePath(root, name));
|
|
69
70
|
const pkg = current['package.json'] ? JSON.parse(current['package.json']) : { name: 'orionplace-storefront', private: true, type: 'module' };
|
|
70
71
|
const dependencies = { ...pkg.devDependencies, ...pkg.dependencies };
|
|
72
|
+
if (action === 'update' && (!dependencies.nuxt || !current['orionplace/settings.json'] || !current['orionplace/.generated.json'])) {
|
|
73
|
+
throw new Error('Update requires an initialized Orionplace Nuxt project. Run init first.');
|
|
74
|
+
}
|
|
71
75
|
if (!dependencies.nuxt && ['next', '@sveltejs/kit'].some(name => dependencies[name])) throw new Error('This package sets up Nuxt projects only.');
|
|
72
76
|
if (dependencies.nuxt && !/^(?:[~^]|>=)?4(?:\.|$)/.test(dependencies.nuxt)) {
|
|
73
77
|
throw new Error('This release supports Nuxt 4. Upgrade your Nuxt dependency before setup.');
|
|
@@ -98,6 +102,12 @@ export async function setupProject({ cwd = process.cwd(), action = 'set', field,
|
|
|
98
102
|
settings.frontends = [...new Set(frontends.map(frontendDomain))];
|
|
99
103
|
if (settings.frontend && !settings.frontends.includes(settings.frontend)) settings.frontends.unshift(settings.frontend);
|
|
100
104
|
if (field === 'backend') settings.backend = setting;
|
|
105
|
+
if (field === 'mimic') {
|
|
106
|
+
if (!settings.frontends.includes(setting)) {
|
|
107
|
+
throw new Error(`Add this frontend before selecting it for MIMIC: ${setting}`);
|
|
108
|
+
}
|
|
109
|
+
settings.frontend = setting;
|
|
110
|
+
}
|
|
101
111
|
if (field === 'frontend') {
|
|
102
112
|
if (action === 'remove') {
|
|
103
113
|
if (!settings.frontends.includes(setting)) throw new Error(`Frontend is not managed by this project: ${setting}`);
|
|
@@ -143,24 +153,56 @@ export async function setupProject({ cwd = process.cwd(), action = 'set', field,
|
|
|
143
153
|
pkg.scripts['orionplace:frontend'] ||= 'orionplace-nuxt set frontend';
|
|
144
154
|
pkg.scripts['orionplace:add-frontend'] ||= 'orionplace-nuxt add frontend';
|
|
145
155
|
pkg.scripts['orionplace:remove-frontend'] ||= 'orionplace-nuxt remove frontend';
|
|
156
|
+
pkg.scripts['orionplace:mimic'] ||= 'orionplace-nuxt set mimic';
|
|
157
|
+
pkg.scripts['orionplace:help'] ||= 'orionplace-nuxt help';
|
|
158
|
+
pkg.scripts['orionplace:update'] ||= 'orionplace-nuxt update';
|
|
146
159
|
pkg.dependencies ||= {};
|
|
147
160
|
pkg.dependencies['orionplace-nuxt'] ||= dependencies['orionplace-nuxt'] || `^${metadata.version}`;
|
|
161
|
+
if (action === 'update') {
|
|
162
|
+
pkg.dependencies['orionplace-nuxt'] = metadata.version;
|
|
163
|
+
if (pkg.devDependencies) delete pkg.devDependencies['orionplace-nuxt'];
|
|
164
|
+
}
|
|
148
165
|
if (isNewNuxt) {
|
|
149
166
|
pkg.dependencies.nuxt = '^4.5.0';
|
|
150
167
|
pkg.dependencies.vue ||= '^3.5.0';
|
|
151
168
|
pkg.dependencies['vue-router'] ||= '^5.0.0';
|
|
152
169
|
}
|
|
170
|
+
// Install the complete editable AppLoad source. Never replace a customer's app.
|
|
171
|
+
const appPath = 'app/app.vue';
|
|
172
|
+
const existingApp = await read(await safePath(root, appPath));
|
|
173
|
+
const installApp = existingApp === null || Boolean(previous[appPath]);
|
|
174
|
+
const templateRoot = new URL('../templates/', import.meta.url);
|
|
175
|
+
async function planTemplates(directory = '') {
|
|
176
|
+
for (const entry of await readdir(new URL(directory, templateRoot), { withFileTypes: true })) {
|
|
177
|
+
const name = directory + entry.name;
|
|
178
|
+
if (entry.isDirectory()) { await planTemplates(name + '/'); continue; }
|
|
179
|
+
if (name === appPath && !installApp) continue;
|
|
180
|
+
await plan(name, await readFile(new URL(name, templateRoot), 'utf8'), { managed: true, preserve: true });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await planTemplates();
|
|
184
|
+
if (installApp && !dependencies.sass && !dependencies['sass-embedded']) {
|
|
185
|
+
pkg.devDependencies ||= {};
|
|
186
|
+
pkg.devDependencies.sass = '^1.102.0';
|
|
187
|
+
}
|
|
188
|
+
if (!await read(await safePath(root, 'tsconfig.json'))) {
|
|
189
|
+
await plan('tsconfig.json', JSON.stringify({ files: [], references: [
|
|
190
|
+
{ path: './.nuxt/tsconfig.app.json' }, { path: './.nuxt/tsconfig.server.json' },
|
|
191
|
+
{ path: './.nuxt/tsconfig.shared.json' }, { path: './.nuxt/tsconfig.node.json' },
|
|
192
|
+
] }, null, 2) + '\n', { managed: true });
|
|
193
|
+
}
|
|
153
194
|
await plan('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
154
195
|
for (const file of ['marketplace.mjs', 'api.mjs', 'domain.mjs']) {
|
|
155
196
|
await plan(`orionplace/runtime/${file}`, await readFile(new URL(`../runtime/${file}`, import.meta.url), 'utf8'), { managed: true, preserve: true });
|
|
156
197
|
}
|
|
157
198
|
await plan('orionplace/README.md', projectReadme(settings), { managed: true, preserve: true });
|
|
158
|
-
|
|
159
|
-
|
|
199
|
+
// A new project starts explicitly empty; keep a pre-existing manual setting on init.
|
|
200
|
+
const hasMimicSetting = /^\s*(?:export\s+)?MIMIC_FRONT_DOMAIN\s*=/m.test(current['.env'] || '');
|
|
201
|
+
if (action !== 'update' && (settings.frontend || oldFrontend || !hasMimicSetting)) {
|
|
202
|
+
await plan('.env', updateEnvironment(current['.env'], settings.frontend || ''));
|
|
160
203
|
}
|
|
161
|
-
if (settings.
|
|
162
|
-
|
|
163
|
-
const mappings = action === 'remove' ? []
|
|
204
|
+
if (settings.backend && action !== 'update') {
|
|
205
|
+
const mappings = action === 'remove' || field === 'mimic' ? []
|
|
164
206
|
: field === 'frontend' ? [setting] : settings.frontends;
|
|
165
207
|
for (const domain of mappings) {
|
|
166
208
|
await plan(`sitemaps/${domain}.cfg`, settings.backend + '\n', { managed: true });
|
|
@@ -172,7 +214,7 @@ export async function setupProject({ cwd = process.cwd(), action = 'set', field,
|
|
|
172
214
|
if (!ignore.split(/\r?\n/).includes(entry)) ignore = ignore.replace(/\n*$/, '\n') + entry + '\n';
|
|
173
215
|
}
|
|
174
216
|
await plan('.gitignore', ignore);
|
|
175
|
-
await plan('orionplace/settings.json', JSON.stringify(settings, null, 2) + '\n');
|
|
217
|
+
if (action !== 'update') await plan('orionplace/settings.json', JSON.stringify(settings, null, 2) + '\n');
|
|
176
218
|
await plan('orionplace/.generated.json', JSON.stringify(manifest, null, 2) + '\n');
|
|
177
219
|
if (!dryRun) {
|
|
178
220
|
for (const [name, content] of changes) {
|
package/src/update.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { setupProject } from './setup.mjs';
|
|
4
|
+
|
|
5
|
+
export function runNpm(args, cwd) {
|
|
6
|
+
const npmCli = process.env.npm_execpath;
|
|
7
|
+
const command = npmCli ? process.execPath : 'npm';
|
|
8
|
+
const commandArgs = npmCli ? [npmCli, ...args] : args;
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const child = spawn(command, commandArgs, { cwd, stdio: 'inherit', shell: false });
|
|
11
|
+
child.once('error', reject);
|
|
12
|
+
child.once('exit', (code, signal) => code === 0 ? resolve() : reject(new Error(
|
|
13
|
+
`npm ${args[0]} failed (${signal || code}).`,
|
|
14
|
+
)));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function updateProject({ cwd = process.cwd(), dryRun = false, local = false, npm = runNpm } = {}) {
|
|
19
|
+
cwd = resolve(cwd);
|
|
20
|
+
// Validate before downloading or writing; the latest CLI repeats preflight with its own templates.
|
|
21
|
+
const preview = await setupProject({ cwd, action: 'update', dryRun: true });
|
|
22
|
+
if (!local) {
|
|
23
|
+
console.log('Checking the latest Orionplace Nuxt release…');
|
|
24
|
+
await npm(['exec', '--yes', '--prefer-online', '--package=orionplace-nuxt@latest', '--',
|
|
25
|
+
'orionplace-nuxt', 'update', '--local', '--cwd', cwd, ...(dryRun ? ['--dry-run'] : [])], cwd);
|
|
26
|
+
return null; // The latest CLI reports its own changes.
|
|
27
|
+
}
|
|
28
|
+
if (dryRun) return preview;
|
|
29
|
+
const result = await setupProject({ cwd, action: 'update' });
|
|
30
|
+
try {
|
|
31
|
+
await npm(['install', '--no-audit', '--no-fund'], cwd);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(`Source files were updated, but dependency installation failed. Run npm install in the project, then retry update. ${error.message}`);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import { useHead } from '#imports';
|
|
4
|
+
import { useOrionplaceAppLoad } from './composables/useOrionplaceAppLoad';
|
|
5
|
+
import type { IAppLoadPathsRecord } from './interfaces/orionplace';
|
|
6
|
+
|
|
7
|
+
useHead({ title: 'Marketplace · AppLoad debug' });
|
|
8
|
+
const { data, status, error, refresh } = await useOrionplaceAppLoad();
|
|
9
|
+
const page = computed(() => data.value?.response.page_data);
|
|
10
|
+
const init = computed(() => page.value?.init);
|
|
11
|
+
const countEndpoints = (routes?: IAppLoadPathsRecord) =>
|
|
12
|
+
Object.values(routes?.paths ?? {}).reduce((total, section) => total + Object.keys(section).length, 0);
|
|
13
|
+
const stateLabel = computed(() => ({ idle: 'Not started', pending: 'Loading', success: 'Loaded', error: 'Failed' })[status.value]);
|
|
14
|
+
const failure = computed(() => error.value?.data);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<main class="bootstrap">
|
|
19
|
+
<header>
|
|
20
|
+
<p class="eyebrow">Orionplace · Nuxt starter</p>
|
|
21
|
+
<h1>Your marketplace connection</h1>
|
|
22
|
+
<p>This first page checks initialization through <code>POST /api/appload</code>.
|
|
23
|
+
Replace it with your storefront when the connection is ready.</p>
|
|
24
|
+
</header>
|
|
25
|
+
<section aria-labelledby="status-title" :aria-busy="status === 'pending'">
|
|
26
|
+
<div class="section-heading">
|
|
27
|
+
<h2 id="status-title">AppLoad state</h2>
|
|
28
|
+
<button type="button" :disabled="status === 'pending'" @click="refresh()">Reload AppLoad</button>
|
|
29
|
+
</div>
|
|
30
|
+
<p role="status" aria-live="polite" class="state" :data-state="status">{{ stateLabel }}</p>
|
|
31
|
+
<p v-if="status === 'pending'">Loading initialization data and available API endpoints…</p>
|
|
32
|
+
<div v-else-if="error" role="alert">
|
|
33
|
+
<p>AppLoad could not initialize. {{ failure?.kind === 'validation' ? 'The backend returned form errors.' :
|
|
34
|
+
failure?.kind === 'invalid-response' ? 'The response is missing required initialization data.' :
|
|
35
|
+
failure?.kind === 'network' ? 'The API request did not complete.' : 'The API returned an HTTP error.' }}</p>
|
|
36
|
+
<p v-if="failure?.httpStatus">HTTP status: <code>{{ failure.httpStatus }}</code></p>
|
|
37
|
+
<p>Check your backend, frontend domain and <code>MIMIC_FRONT_DOMAIN</code> using
|
|
38
|
+
<code>npx orionplace-nuxt help</code>. Setup instructions are in <code>orionplace/README.md</code>.</p>
|
|
39
|
+
</div>
|
|
40
|
+
<dl v-else-if="data && init">
|
|
41
|
+
<div><dt>HTTP status</dt><dd>{{ data.httpStatus }}</dd></div>
|
|
42
|
+
<div><dt>Loaded at (UTC)</dt><dd><time :datetime="data.loadedAt">{{ data.loadedAt }}</time></dd></div>
|
|
43
|
+
<div><dt>Session</dt><dd>{{ init.is_authenticated ? 'Signed in' : 'Guest' }}</dd></div>
|
|
44
|
+
<div><dt>Languages</dt><dd>{{ Object.keys(init.locale).length }}</dd></div>
|
|
45
|
+
<div><dt>Currencies</dt><dd>{{ Object.keys(init.currencies).length }}</dd></div>
|
|
46
|
+
<div><dt>Public endpoints</dt><dd>{{ countEndpoints(page?.unauth) }}</dd></div>
|
|
47
|
+
<div><dt>Authenticated endpoints</dt><dd>{{ countEndpoints(page?.auth) }}</dd></div>
|
|
48
|
+
<div><dt>Catalog roots</dt><dd>{{ init.catalog?.length ?? 'Not included' }}</dd></div>
|
|
49
|
+
<div><dt>Initial products</dt><dd>{{ init.products?.length ?? 'Not included' }}</dd></div>
|
|
50
|
+
<div><dt>Tariffs</dt><dd>{{ init.tariffs?.data.length ?? 'Not included' }}</dd></div>
|
|
51
|
+
</dl>
|
|
52
|
+
</section>
|
|
53
|
+
<section aria-labelledby="next-title">
|
|
54
|
+
<h2 id="next-title">Build from typed initialization data</h2>
|
|
55
|
+
<p>The request lives in <code>app/composables/useOrionplaceAppLoad.ts</code>.
|
|
56
|
+
The response and its nested interfaces are in <code>app/interfaces/orionplace/</code>.</p>
|
|
57
|
+
<p>This debug page shows section counts. Account details, keys and backend diagnostics are not displayed.</p>
|
|
58
|
+
<a href="https://orionmarket.place/sandbox">Read the marketplace documentation</a>
|
|
59
|
+
</section>
|
|
60
|
+
</main>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<style lang="scss">
|
|
64
|
+
body { margin: 0; background: #f5f6f8; color: #202b38; font-family: system-ui, sans-serif; }
|
|
65
|
+
.bootstrap {
|
|
66
|
+
max-width: 860px; margin: 0 auto; padding: 48px 24px;
|
|
67
|
+
h1, h2 { font-weight: 400; line-height: 1.25; }
|
|
68
|
+
h1 { font-size: 32px; margin: 12px 0; }
|
|
69
|
+
h2 { font-size: 21px; margin: 0; }
|
|
70
|
+
p { line-height: 1.7; }
|
|
71
|
+
.eyebrow { color: #596777; font-size: 14px; }
|
|
72
|
+
section { margin-top: 28px; padding: 24px; border: 1px solid #d9dfe6; border-radius: 10px; background: #fff; }
|
|
73
|
+
.section-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
|
74
|
+
.state { display: inline-block; padding: 4px 12px; border-radius: 4px; background: #eaf0f5;
|
|
75
|
+
&[data-state='success'] { background: #e6f2ec; color: #245c40; }
|
|
76
|
+
&[data-state='error'] { background: #fbeceb; color: #8c302a; }
|
|
77
|
+
}
|
|
78
|
+
button { font: inherit; color: inherit; background: #f5f6f8; border: 1px solid #bfc9d4; border-radius: 5px; padding: 9px 14px; cursor: pointer;
|
|
79
|
+
&:disabled { opacity: .6; cursor: wait; }
|
|
80
|
+
&:hover:not(:disabled) { background: #eaf0f5; }
|
|
81
|
+
}
|
|
82
|
+
a { color: #245786; text-underline-offset: 3px; }
|
|
83
|
+
:focus-visible { outline: 2px solid #245786; outline-offset: 4px; }
|
|
84
|
+
code { font-size: .9em; overflow-wrap: anywhere; }
|
|
85
|
+
dl { margin-bottom: 0; div { display: grid; grid-template-columns: minmax(140px, 1fr) 2fr; gap: 12px; padding: 12px 0; border-top: 1px solid #e8ecf0; }
|
|
86
|
+
dt { color: #596777; } dd { margin: 0; overflow-wrap: anywhere; }
|
|
87
|
+
}
|
|
88
|
+
@media (max-width: 520px) { padding: 24px 16px; section { padding: 18px; } dl div { grid-template-columns: 1fr; gap: 4px; } }
|
|
89
|
+
}
|
|
90
|
+
</style>
|