@minamocms/framework 0.10.1 → 0.10.2
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 +6 -0
- package/README.md +1 -1
- package/dist/create-site/cli.js +1 -1
- package/dist/create-site/mint-token-cli.js +1 -1
- package/dist/create-site/mint-token.js +1 -1
- package/dist/create-site/template/AGENTS.md +1 -1
- package/dist/media/drivers/local-fs-driver.js +13 -0
- package/dist/server.js +1 -1
- package/dist/site-sync/cli-helpers.d.ts +1 -1
- package/dist/site-sync/cli-helpers.js +2 -2
- package/dist/site-sync/prompts.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this package are documented here. Format loosely follows
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.2] - 2026-11-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **A site with no `media/` folder serves and stores media normally.** `media/` is git-ignored, so a site deployed by a host that builds from its repository (Railway, for one) starts without it. Until now every request for a media file there answered 500 instead of 404, and the first upload failed the same way. A missing folder now reads as an empty one, and the first upload creates it.
|
|
12
|
+
|
|
7
13
|
## [0.10.1] - 2026-10-15
|
|
8
14
|
|
|
9
15
|
### Changed
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ npm install
|
|
|
54
54
|
npm start
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Visit `http://localhost:3000`. `create-site` prints a real
|
|
57
|
+
Visit `http://localhost:3000`. `create-site` prints a real access token to
|
|
58
58
|
your terminal when it scaffolds the site - that's what the Minamo admin
|
|
59
59
|
(or a plain `curl`) uses to authenticate against the site's
|
|
60
60
|
`/v1/` API. Lost it, or need a second one? See the `mint-token` command in
|
package/dist/create-site/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ try {
|
|
|
11
11
|
const { raw } = scaffoldSite(targetDir);
|
|
12
12
|
console.log(`Created a new site at ${targetDir}`);
|
|
13
13
|
console.log('');
|
|
14
|
-
console.log('Your
|
|
14
|
+
console.log('Your access token (save this now - it will not be shown again):');
|
|
15
15
|
console.log(` ${raw}`);
|
|
16
16
|
console.log('');
|
|
17
17
|
console.log('Next steps:');
|
|
@@ -12,7 +12,7 @@ try {
|
|
|
12
12
|
const scopes = parseScopes(scopesArg);
|
|
13
13
|
const siteDir = resolve(process.cwd(), targetArg);
|
|
14
14
|
const { raw } = mintToken(siteDir, scopes);
|
|
15
|
-
console.log('New
|
|
15
|
+
console.log('New access token (save this now - it will not be shown again):');
|
|
16
16
|
console.log(` ${raw}`);
|
|
17
17
|
console.log('');
|
|
18
18
|
console.log(`Scopes: ${scopes.join(', ')}`);
|
|
@@ -44,6 +44,6 @@ export function mintToken(siteDir, scopes) {
|
|
|
44
44
|
tokens.push({ hash: token.hash, scopes });
|
|
45
45
|
config.tokens = tokens;
|
|
46
46
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
47
|
-
commitPaths(siteDir, ['vhost/site.config.json'], 'chore: mint a new
|
|
47
|
+
commitPaths(siteDir, ['vhost/site.config.json'], 'chore: mint a new access token', CHECKPOINT_AUTHOR);
|
|
48
48
|
return { raw: token.raw };
|
|
49
49
|
}
|
|
@@ -452,7 +452,7 @@ npm run dev # same, plus auto-restart whenever a theme/ file changes - u
|
|
|
452
452
|
npm run stop # stops a site started by any of the above, from any terminal
|
|
453
453
|
```
|
|
454
454
|
|
|
455
|
-
A site is deployed once; after that, `npm run pull` and `npm run push` move changes between this copy and the live site. `npm run pull` makes this copy match the live site for whatever is chosen - content (pages, menus, redirects, site settings, images), theme, or both - asking for the address and
|
|
455
|
+
A site is deployed once; after that, `npm run pull` and `npm run push` move changes between this copy and the live site. `npm run pull` makes this copy match the live site for whatever is chosen - content (pages, menus, redirects, site settings, images), theme, or both - asking for the address and access token (or `npm run pull -- <its URL>` with `CMS_TOKEN` set). It refuses while the parts it would overwrite have uncommitted changes, and commits nothing. `npm run push` sends back only what changed here since the last pull: anything an editor changed on the live site since then stops the push and is listed instead of overwritten, it shows everything it will change, and it asks for the site's address to be typed first. After `npm run upgrade` it can also upgrade the live site's CMS. Never run push on a user's behalf without them confirming.
|
|
456
456
|
|
|
457
457
|
`npm run dev` only watches `theme/` - content changes (via the API) already show up on the next request with no restart needed, so there's nothing to gain watching `content/` too.
|
|
458
458
|
|
|
@@ -31,6 +31,11 @@ export function openLocalFsMediaDriver(root) {
|
|
|
31
31
|
});
|
|
32
32
|
},
|
|
33
33
|
async put(filename, bytes) {
|
|
34
|
+
// The root first: sanitisePath resolves the root's real path, which
|
|
35
|
+
// fails outright on a fresh deploy whose media folder was never
|
|
36
|
+
// created (media/ is git-ignored, so a host building from the
|
|
37
|
+
// repository has none).
|
|
38
|
+
mkdirSync(root, { recursive: true });
|
|
34
39
|
const resolved = sanitisePath(root, filename);
|
|
35
40
|
// Harmless no-op for a flat, content-addressed namespace (media
|
|
36
41
|
// filenames never contain subdirectories) - matches
|
|
@@ -45,6 +50,11 @@ export function openLocalFsMediaDriver(root) {
|
|
|
45
50
|
writeFileSync(resolved, bytes);
|
|
46
51
|
},
|
|
47
52
|
async get(filename) {
|
|
53
|
+
// No media folder yet is no media yet: nothing to find, and a read
|
|
54
|
+
// has no business creating the folder.
|
|
55
|
+
if (!existsSync(root)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
48
58
|
let resolved;
|
|
49
59
|
try {
|
|
50
60
|
resolved = sanitisePath(root, filename);
|
|
@@ -70,6 +80,9 @@ export function openLocalFsMediaDriver(root) {
|
|
|
70
80
|
// reading as "already didn't exist", matching
|
|
71
81
|
// manage-redirects.ts's own not-found-is-a-distinct-reason
|
|
72
82
|
// precedent.
|
|
83
|
+
if (!existsSync(root)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
73
86
|
const resolved = sanitisePath(root, filename);
|
|
74
87
|
if (!existsSync(resolved)) {
|
|
75
88
|
return false;
|
package/dist/server.js
CHANGED
|
@@ -219,7 +219,7 @@ export async function startServer(siteRoot, options = {}) {
|
|
|
219
219
|
try {
|
|
220
220
|
tunnel = await (options.startTunnel ?? startDevTunnel)(serverConfig.port);
|
|
221
221
|
console.log(`Tunnel open: ${tunnel.url}`);
|
|
222
|
-
console.log('This URL is publicly reachable for as long as this process runs - anyone with it (and a valid
|
|
222
|
+
console.log('This URL is publicly reachable for as long as this process runs - anyone with it (and a valid access token) can reach this site. Do not leave it running unattended.');
|
|
223
223
|
}
|
|
224
224
|
catch (error) {
|
|
225
225
|
// The site itself is already up and correctly serving locally -
|
|
@@ -5,7 +5,7 @@ export interface SyncArgs {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function parseSyncArgs(argv: string[]): SyncArgs;
|
|
7
7
|
export declare function ask(question: string, hidden?: boolean): Promise<string>;
|
|
8
|
-
export declare const TOKEN_HELP = "The token is the live site's
|
|
8
|
+
export declare const TOKEN_HELP = "The token is the live site's access token (the same one the admin uses), with the \"content\" and \"media\" scopes.";
|
|
9
9
|
export declare function resolveToken(args: SyncArgs, siteUrl: string): Promise<string>;
|
|
10
10
|
export interface PartOption {
|
|
11
11
|
available: boolean;
|
|
@@ -23,11 +23,11 @@ export async function ask(question, hidden = false) {
|
|
|
23
23
|
throw error;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
export const TOKEN_HELP = 'The token is the live site\'s
|
|
26
|
+
export const TOKEN_HELP = 'The token is the live site\'s access token (the same one the admin uses), with the "content" and "media" scopes.';
|
|
27
27
|
// CMS_TOKEN and --token still work for scripts, but either leaves the
|
|
28
28
|
// token in shell history; asked for, it goes nowhere.
|
|
29
29
|
export async function resolveToken(args, siteUrl) {
|
|
30
|
-
const token = args.token ?? process.env.CMS_TOKEN ?? (await ask(`
|
|
30
|
+
const token = args.token ?? process.env.CMS_TOKEN ?? (await ask(`Access token for ${siteUrl} (hidden): `, true));
|
|
31
31
|
if (!token) {
|
|
32
32
|
console.error('No token entered.');
|
|
33
33
|
console.error(TOKEN_HELP);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Asks questions on the terminal - the live site's address, then its
|
|
2
|
-
//
|
|
2
|
+
// access token without showing it. A token typed on the command line (or
|
|
3
3
|
// in CMS_TOKEN=... before it) ends up in shell history; typed here, it
|
|
4
4
|
// goes nowhere but this process.
|
|
5
5
|
export class PromptCancelledError extends Error {
|