@canra/cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/canra.js +9 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,8 @@ canra search json
|
|
|
34
34
|
| `canra logout` | Remove saved credentials |
|
|
35
35
|
| `canra version` / `canra help` | Version and usage |
|
|
36
36
|
|
|
37
|
+
`<owner>` is a user or organization name in any case: `Canra/json` and `canra/json` are the same spec, saved under the lowercase owner in `.canra/specs/`.
|
|
38
|
+
|
|
37
39
|
## Vendoring specs
|
|
38
40
|
|
|
39
41
|
Commit `.canra/` to source control. The saved specs are the record of which spec versions your project implemented (a version is exact and immutable). After `canra update`, `git diff .canra/` shows what changed in each spec.
|
package/canra.js
CHANGED
|
@@ -89,11 +89,11 @@ async function fetchRegistry(url, opts) {
|
|
|
89
89
|
|
|
90
90
|
// --- Spec references ---
|
|
91
91
|
|
|
92
|
-
// Mirror the server's naming rules (
|
|
92
|
+
// Mirror the server's naming rules (username.js USERNAME_RE and grandfathered names, specs-api.js
|
|
93
93
|
// NAME_RE). Owner and name become local path segments under .canra/specs/, so
|
|
94
94
|
// anything path-like ("..", ".", backslashes) must be rejected here — the CLI is
|
|
95
95
|
// meant to be driven by agents whose arguments may come from untrusted content.
|
|
96
|
-
const OWNER_RE = /^[a-z][a-z0-9_-]{1,38}[a-z0-9]
|
|
96
|
+
const OWNER_RE = /^(?:(?=.{1,39}$)[a-z0-9]+(?:-[a-z0-9]+)*|[a-z][a-z0-9_-]{1,38}[a-z0-9])$/i;
|
|
97
97
|
const NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
98
98
|
const SUGGESTION_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
99
99
|
|
|
@@ -197,7 +197,7 @@ function refuseSymlinks(paths) {
|
|
|
197
197
|
|
|
198
198
|
function localSpecPaths(owner, name) {
|
|
199
199
|
const specsDir = path.join(LOCAL_DIR, "specs");
|
|
200
|
-
const ownerDir = path.join(specsDir, owner);
|
|
200
|
+
const ownerDir = path.join(specsDir, owner.toLowerCase());
|
|
201
201
|
return [LOCAL_DIR, specsDir, ownerDir, path.join(ownerDir, name)];
|
|
202
202
|
}
|
|
203
203
|
|
|
@@ -207,8 +207,10 @@ function frontmatterFunctionCount(text) {
|
|
|
207
207
|
return list ? (list[1].match(/"(?:[^"\\]|\\.)*"/g) || []).length : 0;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
// Owner directories are lowercased everywhere (here, localSpecPaths, deleteLocalSpec) so Canra/json and
|
|
211
|
+
// canra/json are one local spec on case-sensitive and case-insensitive filesystems alike.
|
|
210
212
|
function localSpecDir(owner, name) {
|
|
211
|
-
return path.join(LOCAL_DIR, "specs", owner, name);
|
|
213
|
+
return path.join(LOCAL_DIR, "specs", owner.toLowerCase(), name);
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
async function downloadSpec(registry, { owner, name, version }) {
|
|
@@ -674,7 +676,7 @@ function deleteLocalSpec(owner, name) {
|
|
|
674
676
|
if (!dir.startsWith(specsRoot + path.sep)) throw new Error(`unexpected local path for ${owner}/${name}`);
|
|
675
677
|
refuseSymlinks(localSpecPaths(owner, name));
|
|
676
678
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
677
|
-
const ownerDir = path.join(LOCAL_DIR, "specs", owner);
|
|
679
|
+
const ownerDir = path.join(LOCAL_DIR, "specs", owner.toLowerCase());
|
|
678
680
|
if (fs.readdirSync(ownerDir).length === 0) fs.rmdirSync(ownerDir);
|
|
679
681
|
}
|
|
680
682
|
|
|
@@ -710,7 +712,7 @@ async function cmdUpdate(specArg) {
|
|
|
710
712
|
|
|
711
713
|
let refs = scanLocalSpecs();
|
|
712
714
|
if (only) {
|
|
713
|
-
refs = refs.filter((r) => r.owner === only.owner && r.name === only.name);
|
|
715
|
+
refs = refs.filter((r) => r.owner.toLowerCase() === only.owner.toLowerCase() && r.name === only.name);
|
|
714
716
|
if (refs.length === 0) {
|
|
715
717
|
console.error(`Error: ${only.owner}/${only.name} is not fetched locally. Run \`canra fetch\` first.`);
|
|
716
718
|
process.exit(1);
|
|
@@ -741,7 +743,7 @@ async function cmdUpdate(specArg) {
|
|
|
741
743
|
if (!OWNER_RE.test(toOwner) || (target && target[2] !== row.name)) {
|
|
742
744
|
throw new Error(`unexpected move target '${printable(row.moved_to)}'`);
|
|
743
745
|
}
|
|
744
|
-
const moving = toOwner !== row.owner;
|
|
746
|
+
const moving = toOwner.toLowerCase() !== row.owner.toLowerCase();
|
|
745
747
|
if (moving && fs.existsSync(localSpecDir(toOwner, row.name))) {
|
|
746
748
|
throw new Error(`${toOwner}/${row.name} is already fetched locally; remove one of the two copies`);
|
|
747
749
|
}
|