@gigamusic/links 0.2.0 → 1.0.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 +12 -10
- package/package.json +6 -25
- package/src/index.ts +2 -52
- package/src/platforms/detect.ts +10 -3
- package/src/server.ts +0 -7
- package/dist/cli.js +0 -191
- package/dist/cli.js.map +0 -1
- package/src/cli/index.ts +0 -95
- package/src/cli/paths.ts +0 -11
- package/src/cli/sync.ts +0 -169
- package/src/client.ts +0 -10
- package/src/components/LinkButton.tsx +0 -20
- package/src/components/LinkPageHeader.tsx +0 -35
- package/src/components/LinkPageView.tsx +0 -64
- package/src/components/admin/DeleteLinkPageButton.tsx +0 -52
- package/src/components/admin/LinkPageForm.tsx +0 -490
- package/src/components/admin/NewLinkPageForm.tsx +0 -152
- package/src/components/url-helpers.ts +0 -18
- package/src/pages/AdminEditLinkPagePage.tsx +0 -57
- package/src/pages/AdminLinkPagesIndexPage.tsx +0 -107
- package/src/pages/AdminNewLinkPagePage.tsx +0 -26
- package/src/pages/PublicLinkPage.tsx +0 -117
- package/src/slots-context.tsx +0 -46
- package/src/slots.ts +0 -36
package/README.md
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
# @gigamusic/links
|
|
2
2
|
|
|
3
|
-
Linktree-style per-release link pages for
|
|
4
|
-
|
|
5
|
-
## Install
|
|
3
|
+
Linktree-style per-release link pages for the gigamusic platform. Ships its own Prisma fragment (`LinkPage`, `LinkPageItem`), a typed queries factory, admin API handler factories, platform-detection helpers, and a small SVG `LinkPlatformIcon` component.
|
|
6
4
|
|
|
7
5
|
```ts
|
|
8
6
|
import {
|
|
9
7
|
createLinkPageQueries,
|
|
10
|
-
LinkPageView,
|
|
11
|
-
createPublicLinkPage,
|
|
12
|
-
createAdminLinkPagesHandlers,
|
|
13
8
|
detectLinkPlatform,
|
|
9
|
+
PLATFORM_LABELS,
|
|
14
10
|
LinkPlatformIcon,
|
|
11
|
+
type KnownLinkPlatform,
|
|
15
12
|
} from "@gigamusic/links";
|
|
13
|
+
|
|
14
|
+
// Admin handler factories live on the /server subpath
|
|
15
|
+
import {
|
|
16
|
+
createAdminLinkPagesHandlers,
|
|
17
|
+
createAdminLinkPageByIdHandlers,
|
|
18
|
+
createAdminLinkPageItemsHandlers,
|
|
19
|
+
} from "@gigamusic/links/server";
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
The
|
|
22
|
+
The public link-page UI is the consumer's responsibility — fetch with `createLinkPageQueries(prisma).getPublicLinkPageBySlug(slug)` and render whatever you want.
|
|
19
23
|
|
|
20
24
|
## Prisma fragment
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
The `link-page.prisma` fragment ships under `node_modules/@gigamusic/links/prisma/`. Copy it into your `prisma/schema/` directory (Prisma 6 `prismaSchemaFolder` mode) once per upgrade.
|
|
23
27
|
|
|
24
28
|
The fragment references the `Release` model owned by `@gigamusic/db`. Prisma requires both sides of a relation to be declared, so consumers must add the inverse on their copy of `Release`:
|
|
25
29
|
|
|
@@ -30,5 +34,3 @@ model Release {
|
|
|
30
34
|
linkPages LinkPage[]
|
|
31
35
|
}
|
|
32
36
|
```
|
|
33
|
-
|
|
34
|
-
If your `Release` is shipped verbatim from `@gigamusic/db`'s `10-release.prisma`, add a thin override file at `99-release-link-pages.prisma` that re-declares the model with the extra field, or extend the upstream fragment when re-syncing.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gigamusic/links",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Editable Linktree-style link pages for gigamusic artist sites. Ships a Prisma fragment,
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Editable Linktree-style link pages for gigamusic artist sites. Ships a Prisma fragment, admin API handler factories, and platform-detection helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -9,39 +9,27 @@
|
|
|
9
9
|
"directory": "packages/links"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
|
-
"main": "./src/index.ts",
|
|
13
|
-
"types": "./src/index.ts",
|
|
14
12
|
"exports": {
|
|
15
13
|
".": {
|
|
16
14
|
"types": "./src/index.ts",
|
|
17
15
|
"default": "./src/index.ts"
|
|
18
16
|
},
|
|
19
|
-
"./client": {
|
|
20
|
-
"types": "./src/client.ts",
|
|
21
|
-
"default": "./src/client.ts"
|
|
22
|
-
},
|
|
23
17
|
"./server": {
|
|
24
18
|
"types": "./src/server.ts",
|
|
25
19
|
"default": "./src/server.ts"
|
|
26
20
|
}
|
|
27
21
|
},
|
|
28
|
-
"bin": {
|
|
29
|
-
"gigamusic-links": "./dist/cli.js"
|
|
30
|
-
},
|
|
31
22
|
"files": [
|
|
32
23
|
"src",
|
|
33
24
|
"prisma",
|
|
34
|
-
"dist",
|
|
35
25
|
"README.md"
|
|
36
26
|
],
|
|
37
27
|
"publishConfig": {
|
|
38
28
|
"access": "public"
|
|
39
29
|
},
|
|
40
30
|
"dependencies": {
|
|
41
|
-
"@gigamusic/core": "0.
|
|
42
|
-
"@gigamusic/
|
|
43
|
-
"@gigamusic/config": "0.2.0",
|
|
44
|
-
"@gigamusic/db": "0.2.0"
|
|
31
|
+
"@gigamusic/core": "1.0.0",
|
|
32
|
+
"@gigamusic/db": "1.0.0"
|
|
45
33
|
},
|
|
46
34
|
"peerDependencies": {
|
|
47
35
|
"@prisma/client": ">=6",
|
|
@@ -51,26 +39,19 @@
|
|
|
51
39
|
},
|
|
52
40
|
"devDependencies": {
|
|
53
41
|
"@prisma/client": "^6.1.0",
|
|
54
|
-
"@testing-library/dom": "^10.4.0",
|
|
55
|
-
"@testing-library/react": "^16.1.0",
|
|
56
42
|
"@types/node": "^22.10.5",
|
|
57
43
|
"@types/react": "^19.0.0",
|
|
58
|
-
"@types/react-dom": "^19.0.0",
|
|
59
|
-
"jsdom": "^25.0.1",
|
|
60
44
|
"next": "^16.0.0",
|
|
45
|
+
"prisma": "^6.1.0",
|
|
61
46
|
"react": "^19.0.0",
|
|
62
|
-
"react-dom": "^19.0.0",
|
|
63
|
-
"tsup": "^8.3.5",
|
|
64
47
|
"typescript": "^5.7.3",
|
|
65
48
|
"vitest": "^2.1.8"
|
|
66
49
|
},
|
|
67
50
|
"scripts": {
|
|
68
|
-
"build": "pnpm run prisma:generate && tsup",
|
|
69
|
-
"dev": "tsup --watch",
|
|
70
51
|
"lint": "eslint src",
|
|
71
52
|
"test": "pnpm run prisma:generate && vitest run",
|
|
72
53
|
"typecheck": "pnpm run prisma:generate && tsc --noEmit",
|
|
73
54
|
"prisma:generate": "prisma generate --schema=./prisma",
|
|
74
|
-
"clean": "rm -rf
|
|
55
|
+
"clean": "rm -rf .turbo *.tsbuildinfo"
|
|
75
56
|
}
|
|
76
57
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Public entry — server-safe types, queries, and platform helpers.
|
|
2
|
+
// Admin handler factories live on the `./server` subpath.
|
|
3
3
|
|
|
4
4
|
// ── Types ────────────────────────────────────────────────────────────────
|
|
5
5
|
export type {
|
|
@@ -20,56 +20,6 @@ export { detectLinkPlatform, PLATFORM_LABELS } from "./platforms/detect";
|
|
|
20
20
|
export type { KnownLinkPlatform } from "./platforms/detect";
|
|
21
21
|
export { LinkPlatformIcon } from "./platforms/icon";
|
|
22
22
|
|
|
23
|
-
// ── Slots ────────────────────────────────────────────────────────────────
|
|
24
|
-
export type {
|
|
25
|
-
LinksSlots,
|
|
26
|
-
LinkButtonProps,
|
|
27
|
-
LinkPageHeaderProps,
|
|
28
|
-
} from "./slots";
|
|
29
|
-
|
|
30
|
-
// ── Public render surface ───────────────────────────────────────────────
|
|
31
|
-
export { LinkPageView } from "./components/LinkPageView";
|
|
32
|
-
export { DefaultLinkButton } from "./components/LinkButton";
|
|
33
|
-
export { DefaultLinkPageHeader } from "./components/LinkPageHeader";
|
|
34
|
-
|
|
35
|
-
// ── Public page (per-slug landing) ──────────────────────────────────────
|
|
36
|
-
export {
|
|
37
|
-
PublicLinkPage,
|
|
38
|
-
createPublicLinkPage,
|
|
39
|
-
createPublicLinkPageMetadata,
|
|
40
|
-
publicLinkPageMetadata,
|
|
41
|
-
registerLinkPageMetadata,
|
|
42
|
-
} from "./pages/PublicLinkPage";
|
|
43
|
-
export type {
|
|
44
|
-
PublicLinkPageProps,
|
|
45
|
-
CreatePublicLinkPageDeps,
|
|
46
|
-
PublicLinkPageMetadataDeps,
|
|
47
|
-
PublicLinkPageMetadataArgs,
|
|
48
|
-
} from "./pages/PublicLinkPage";
|
|
49
|
-
|
|
50
|
-
// ── Admin page bodies ───────────────────────────────────────────────────
|
|
51
|
-
export {
|
|
52
|
-
AdminLinkPagesIndexPage,
|
|
53
|
-
createAdminLinkPagesIndexPage,
|
|
54
|
-
} from "./pages/AdminLinkPagesIndexPage";
|
|
55
|
-
export type { AdminLinkPagesIndexPageProps } from "./pages/AdminLinkPagesIndexPage";
|
|
56
|
-
export { AdminNewLinkPagePage } from "./pages/AdminNewLinkPagePage";
|
|
57
|
-
export type { AdminNewLinkPagePageProps } from "./pages/AdminNewLinkPagePage";
|
|
58
|
-
export { AdminEditLinkPagePage } from "./pages/AdminEditLinkPagePage";
|
|
59
|
-
export type { AdminEditLinkPagePageProps } from "./pages/AdminEditLinkPagePage";
|
|
60
|
-
|
|
61
|
-
// ── Admin form types (so consumers can build their own page wrappers) ───
|
|
62
|
-
export type {
|
|
63
|
-
LinkPageFormPage,
|
|
64
|
-
LinkPageFormItem,
|
|
65
|
-
LinkPageFormReleaseOption,
|
|
66
|
-
} from "./components/admin/LinkPageForm";
|
|
67
|
-
export type { NewLinkPageReleaseOption } from "./components/admin/NewLinkPageForm";
|
|
68
|
-
|
|
69
|
-
// Admin API handler factories live on the `./server` subpath — they import
|
|
70
|
-
// `next/cache`, which Webpack/Turbopack refuse to bundle into a client tree.
|
|
71
|
-
// Use `import { createAdminLinkPagesHandlers } from "@gigamusic/links/server"`.
|
|
72
|
-
|
|
73
23
|
// ── Slug validation helpers ─────────────────────────────────────────────
|
|
74
24
|
export {
|
|
75
25
|
SLUG_PATTERN,
|
package/src/platforms/detect.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export type KnownLinkPlatform =
|
|
2
|
+
| "spotify"
|
|
3
|
+
| "apple-music"
|
|
4
|
+
| "youtube"
|
|
5
|
+
| "soundcloud"
|
|
6
|
+
| "instagram"
|
|
7
|
+
| "facebook"
|
|
8
|
+
| "tiktok"
|
|
9
|
+
| "x"
|
|
10
|
+
| "bandcamp";
|
|
4
11
|
|
|
5
12
|
interface HostPattern {
|
|
6
13
|
platform: KnownLinkPlatform;
|
package/src/server.ts
CHANGED
|
@@ -13,10 +13,3 @@ export type {
|
|
|
13
13
|
LinkPagesAdminDeps,
|
|
14
14
|
RouteHandler,
|
|
15
15
|
} from "./api/handlers";
|
|
16
|
-
export {
|
|
17
|
-
PublicLinkPage,
|
|
18
|
-
createPublicLinkPage,
|
|
19
|
-
createPublicLinkPageMetadata,
|
|
20
|
-
publicLinkPageMetadata,
|
|
21
|
-
registerLinkPageMetadata,
|
|
22
|
-
} from "./pages/PublicLinkPage";
|
package/dist/cli.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, mkdirSync, existsSync, statSync, writeFileSync, readdirSync } from 'fs';
|
|
3
|
-
import { dirname, resolve, join } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
function fragmentsDir(metaUrl) {
|
|
7
|
-
const here = dirname(fileURLToPath(metaUrl));
|
|
8
|
-
return resolve(here, "..", "prisma");
|
|
9
|
-
}
|
|
10
|
-
var SYNC_PREFIX = "20-";
|
|
11
|
-
var VERSION_RE = /AUTOGENERATED by @gigamusic\/links@([^\s]+) on (\d{4}-\d{2}-\d{2})/;
|
|
12
|
-
function listFragmentFiles(sourceDir) {
|
|
13
|
-
return readdirSync(sourceDir).filter((f) => f.endsWith(".prisma") && !f.startsWith("_")).sort();
|
|
14
|
-
}
|
|
15
|
-
function buildHeader(version, today) {
|
|
16
|
-
return `// AUTOGENERATED by @gigamusic/links@${version} on ${today} \u2014 do not edit; re-run \`pnpm gigamusic-links sync\`
|
|
17
|
-
`;
|
|
18
|
-
}
|
|
19
|
-
function readExistingVersion(filePath) {
|
|
20
|
-
if (!existsSync(filePath)) return null;
|
|
21
|
-
const head = readFileSync(filePath, "utf8").split("\n", 2).join("\n");
|
|
22
|
-
const match = VERSION_RE.exec(head);
|
|
23
|
-
return match?.[1] ?? null;
|
|
24
|
-
}
|
|
25
|
-
function compareVersions(a, b) {
|
|
26
|
-
if (a === b) return 0;
|
|
27
|
-
const aParts = a.split(/[.-]/);
|
|
28
|
-
const bParts = b.split(/[.-]/);
|
|
29
|
-
const len = Math.max(aParts.length, bParts.length);
|
|
30
|
-
for (let i = 0; i < len; i++) {
|
|
31
|
-
const ai = parseInt(aParts[i] ?? "0", 10);
|
|
32
|
-
const bi = parseInt(bParts[i] ?? "0", 10);
|
|
33
|
-
if (Number.isNaN(ai) || Number.isNaN(bi)) {
|
|
34
|
-
const as = aParts[i] ?? "";
|
|
35
|
-
const bs = bParts[i] ?? "";
|
|
36
|
-
if (as === bs) continue;
|
|
37
|
-
return as < bs ? -1 : 1;
|
|
38
|
-
}
|
|
39
|
-
if (ai !== bi) return ai < bi ? -1 : 1;
|
|
40
|
-
}
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
function syncFragments(opts, deps) {
|
|
44
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
45
|
-
const target = resolve(cwd, opts.target ?? join("prisma", "schema"));
|
|
46
|
-
const today = deps.today ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
47
|
-
const logger = deps.logger ?? console;
|
|
48
|
-
const header = buildHeader(deps.version, today);
|
|
49
|
-
const written = [];
|
|
50
|
-
const skipped = [];
|
|
51
|
-
if (!opts.dryRun) {
|
|
52
|
-
mkdirSync(target, { recursive: true });
|
|
53
|
-
} else if (!existsSync(target) || !statSync(target).isDirectory()) {
|
|
54
|
-
logger.info(`(dry-run) would create directory ${target}`);
|
|
55
|
-
}
|
|
56
|
-
for (const file of listFragmentFiles(deps.sourceDir)) {
|
|
57
|
-
const srcPath = join(deps.sourceDir, file);
|
|
58
|
-
const destPath = join(target, `${SYNC_PREFIX}${file}`);
|
|
59
|
-
const existingVersion = readExistingVersion(destPath);
|
|
60
|
-
if (existingVersion && compareVersions(existingVersion, deps.version) > 0) {
|
|
61
|
-
skipped.push({
|
|
62
|
-
file: destPath,
|
|
63
|
-
reason: `existing version ${existingVersion} is newer than installed ${deps.version}`
|
|
64
|
-
});
|
|
65
|
-
logger.warn(
|
|
66
|
-
`skip ${destPath} \u2014 existing v${existingVersion} is newer than installed v${deps.version}`
|
|
67
|
-
);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const body = readFileSync(srcPath, "utf8");
|
|
71
|
-
const contents = header + body;
|
|
72
|
-
if (opts.dryRun) {
|
|
73
|
-
logger.info(`(dry-run) would write ${destPath}`);
|
|
74
|
-
} else {
|
|
75
|
-
writeFileSync(destPath, contents, "utf8");
|
|
76
|
-
logger.info(`wrote ${destPath}`);
|
|
77
|
-
}
|
|
78
|
-
written.push(destPath);
|
|
79
|
-
}
|
|
80
|
-
return { written, skipped };
|
|
81
|
-
}
|
|
82
|
-
function doctorFragments(opts, deps) {
|
|
83
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
84
|
-
const target = resolve(cwd, opts.target ?? join("prisma", "schema"));
|
|
85
|
-
const stale = [];
|
|
86
|
-
const missing = [];
|
|
87
|
-
const logger = deps.logger ?? console;
|
|
88
|
-
for (const file of listFragmentFiles(deps.sourceDir)) {
|
|
89
|
-
const destPath = join(target, `${SYNC_PREFIX}${file}`);
|
|
90
|
-
const existingVersion = readExistingVersion(destPath);
|
|
91
|
-
if (existingVersion === null) {
|
|
92
|
-
missing.push(destPath);
|
|
93
|
-
logger.warn(`missing: ${destPath}`);
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (compareVersions(existingVersion, deps.version) < 0) {
|
|
97
|
-
stale.push({
|
|
98
|
-
file: destPath,
|
|
99
|
-
installed: deps.version,
|
|
100
|
-
found: existingVersion
|
|
101
|
-
});
|
|
102
|
-
logger.warn(
|
|
103
|
-
`stale: ${destPath} is v${existingVersion}; installed @gigamusic/links is v${deps.version}`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return { ok: stale.length === 0 && missing.length === 0, stale, missing };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/cli/index.ts
|
|
111
|
-
function parseArgs(argv) {
|
|
112
|
-
const [command = "help", ...rest] = argv;
|
|
113
|
-
const parsed = {
|
|
114
|
-
command: ["sync", "doctor", "help", "version"].includes(command) ? command : "help",
|
|
115
|
-
dryRun: false
|
|
116
|
-
};
|
|
117
|
-
for (let i = 0; i < rest.length; i++) {
|
|
118
|
-
const arg = rest[i];
|
|
119
|
-
if (arg === "--dry-run") parsed.dryRun = true;
|
|
120
|
-
else if (arg === "--target") parsed.target = rest[++i];
|
|
121
|
-
else if (arg?.startsWith("--target=")) parsed.target = arg.slice("--target=".length);
|
|
122
|
-
}
|
|
123
|
-
return parsed;
|
|
124
|
-
}
|
|
125
|
-
function readPackageVersion(metaUrl) {
|
|
126
|
-
try {
|
|
127
|
-
const pkgPath = resolve(fragmentsDir(metaUrl), "..", "package.json");
|
|
128
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
129
|
-
return pkg.version ?? "0.0.0";
|
|
130
|
-
} catch {
|
|
131
|
-
return "0.0.0";
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function printHelp() {
|
|
135
|
-
console.log(`gigamusic-links \u2014 Prisma fragment sync for @gigamusic/links
|
|
136
|
-
|
|
137
|
-
Usage:
|
|
138
|
-
gigamusic-links sync [--target <prismaSchemaFolder>] [--dry-run]
|
|
139
|
-
gigamusic-links doctor [--target <prismaSchemaFolder>]
|
|
140
|
-
gigamusic-links version
|
|
141
|
-
gigamusic-links help
|
|
142
|
-
|
|
143
|
-
Fragments are written with a "20-" prefix so they sort AFTER @gigamusic/db's
|
|
144
|
-
"10-" fragments and BEFORE consumer-owned "99-" files. Default --target is
|
|
145
|
-
./prisma/schema (Prisma 6 prismaSchemaFolder layout).
|
|
146
|
-
`);
|
|
147
|
-
}
|
|
148
|
-
async function main(argv, metaUrl) {
|
|
149
|
-
const args = parseArgs(argv);
|
|
150
|
-
const sourceDir = fragmentsDir(metaUrl);
|
|
151
|
-
const version = readPackageVersion(metaUrl);
|
|
152
|
-
if (args.command === "help") {
|
|
153
|
-
printHelp();
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
if (args.command === "version") {
|
|
157
|
-
console.log(version);
|
|
158
|
-
return 0;
|
|
159
|
-
}
|
|
160
|
-
if (args.command === "sync") {
|
|
161
|
-
syncFragments(
|
|
162
|
-
{ target: args.target, dryRun: args.dryRun },
|
|
163
|
-
{ sourceDir, version }
|
|
164
|
-
);
|
|
165
|
-
return 0;
|
|
166
|
-
}
|
|
167
|
-
if (args.command === "doctor") {
|
|
168
|
-
const result = doctorFragments({ target: args.target }, { sourceDir, version });
|
|
169
|
-
if (!result.ok) {
|
|
170
|
-
console.error(
|
|
171
|
-
`gigamusic-links doctor: ${result.stale.length} stale, ${result.missing.length} missing`
|
|
172
|
-
);
|
|
173
|
-
return 1;
|
|
174
|
-
}
|
|
175
|
-
console.log("gigamusic-links doctor: all fragments are up to date.");
|
|
176
|
-
return 0;
|
|
177
|
-
}
|
|
178
|
-
printHelp();
|
|
179
|
-
return 1;
|
|
180
|
-
}
|
|
181
|
-
main(process.argv.slice(2), import.meta.url).then(
|
|
182
|
-
(code) => process.exit(code),
|
|
183
|
-
(err) => {
|
|
184
|
-
console.error(err);
|
|
185
|
-
process.exit(1);
|
|
186
|
-
}
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
export { main };
|
|
190
|
-
//# sourceMappingURL=cli.js.map
|
|
191
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/paths.ts","../src/cli/sync.ts","../src/cli/index.ts"],"names":["resolve","readFileSync"],"mappings":"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","file":"cli.js","sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { dirname, resolve } from \"node:path\";\n\n/**\n * Locate the shipped `prisma/` fragments directory. At runtime the CLI bundle\n * lives at `<pkg>/dist/cli.js`; the fragments live at `<pkg>/prisma/`.\n */\nexport function fragmentsDir(metaUrl: string): string {\n const here = dirname(fileURLToPath(metaUrl));\n return resolve(here, \"..\", \"prisma\");\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\nexport interface SyncOptions {\n /** Where the consumer's prisma schema folder lives. Defaults to `<cwd>/prisma/schema`. */\n target?: string;\n /** When true, log planned writes without touching the filesystem. */\n dryRun?: boolean;\n /** Override for testing — defaults to process.cwd(). */\n cwd?: string;\n}\n\nexport interface SyncResult {\n written: string[];\n skipped: { file: string; reason: string }[];\n}\n\nexport interface SyncDeps {\n sourceDir: string;\n version: string;\n today?: string;\n logger?: { info: (msg: string) => void; warn: (msg: string) => void };\n}\n\n/**\n * Sync prefix — `20-` orders after `10-` (gigamusic/db) and before `99-`\n * (consumer-owned). Keeping the prefix shared with @gigamusic/db's sort\n * convention so consumers don't need to learn a new rule.\n */\nconst SYNC_PREFIX = \"20-\";\n\nconst VERSION_RE = /AUTOGENERATED by @gigamusic\\/links@([^\\s]+) on (\\d{4}-\\d{2}-\\d{2})/;\n\nexport function listFragmentFiles(sourceDir: string): string[] {\n return readdirSync(sourceDir)\n .filter((f) => f.endsWith(\".prisma\") && !f.startsWith(\"_\"))\n .sort();\n}\n\nfunction buildHeader(version: string, today: string): string {\n return `// AUTOGENERATED by @gigamusic/links@${version} on ${today} — do not edit; re-run \\`pnpm gigamusic-links sync\\`\\n`;\n}\n\nfunction readExistingVersion(filePath: string): string | null {\n if (!existsSync(filePath)) return null;\n const head = readFileSync(filePath, \"utf8\").split(\"\\n\", 2).join(\"\\n\");\n const match = VERSION_RE.exec(head);\n return match?.[1] ?? null;\n}\n\n/**\n * Semver-ish comparison — accepts `x.y.z` or pre-release suffixes. Returns\n * 1/0/-1 like `Array.prototype.sort`. Workspace `workspace:*` and similar\n * non-numeric strings compare equal so we never refuse to overwrite during\n * local dev.\n */\nexport function compareVersions(a: string, b: string): number {\n if (a === b) return 0;\n const aParts = a.split(/[.-]/);\n const bParts = b.split(/[.-]/);\n const len = Math.max(aParts.length, bParts.length);\n for (let i = 0; i < len; i++) {\n const ai = parseInt(aParts[i] ?? \"0\", 10);\n const bi = parseInt(bParts[i] ?? \"0\", 10);\n if (Number.isNaN(ai) || Number.isNaN(bi)) {\n const as = aParts[i] ?? \"\";\n const bs = bParts[i] ?? \"\";\n if (as === bs) continue;\n return as < bs ? -1 : 1;\n }\n if (ai !== bi) return ai < bi ? -1 : 1;\n }\n return 0;\n}\n\nexport function syncFragments(opts: SyncOptions, deps: SyncDeps): SyncResult {\n const cwd = opts.cwd ?? process.cwd();\n const target = resolve(cwd, opts.target ?? join(\"prisma\", \"schema\"));\n const today = deps.today ?? new Date().toISOString().slice(0, 10);\n const logger = deps.logger ?? console;\n const header = buildHeader(deps.version, today);\n\n const written: string[] = [];\n const skipped: { file: string; reason: string }[] = [];\n\n if (!opts.dryRun) {\n mkdirSync(target, { recursive: true });\n } else if (!existsSync(target) || !statSync(target).isDirectory()) {\n logger.info(`(dry-run) would create directory ${target}`);\n }\n\n for (const file of listFragmentFiles(deps.sourceDir)) {\n const srcPath = join(deps.sourceDir, file);\n const destPath = join(target, `${SYNC_PREFIX}${file}`);\n const existingVersion = readExistingVersion(destPath);\n\n if (existingVersion && compareVersions(existingVersion, deps.version) > 0) {\n skipped.push({\n file: destPath,\n reason: `existing version ${existingVersion} is newer than installed ${deps.version}`,\n });\n logger.warn(\n `skip ${destPath} — existing v${existingVersion} is newer than installed v${deps.version}`,\n );\n continue;\n }\n\n const body = readFileSync(srcPath, \"utf8\");\n const contents = header + body;\n\n if (opts.dryRun) {\n logger.info(`(dry-run) would write ${destPath}`);\n } else {\n writeFileSync(destPath, contents, \"utf8\");\n logger.info(`wrote ${destPath}`);\n }\n written.push(destPath);\n }\n\n return { written, skipped };\n}\n\nexport interface DoctorOptions {\n target?: string;\n cwd?: string;\n}\n\nexport interface DoctorResult {\n ok: boolean;\n stale: { file: string; installed: string; found: string }[];\n missing: string[];\n}\n\nexport function doctorFragments(opts: DoctorOptions, deps: SyncDeps): DoctorResult {\n const cwd = opts.cwd ?? process.cwd();\n const target = resolve(cwd, opts.target ?? join(\"prisma\", \"schema\"));\n const stale: { file: string; installed: string; found: string }[] = [];\n const missing: string[] = [];\n const logger = deps.logger ?? console;\n\n for (const file of listFragmentFiles(deps.sourceDir)) {\n const destPath = join(target, `${SYNC_PREFIX}${file}`);\n const existingVersion = readExistingVersion(destPath);\n if (existingVersion === null) {\n missing.push(destPath);\n logger.warn(`missing: ${destPath}`);\n continue;\n }\n if (compareVersions(existingVersion, deps.version) < 0) {\n stale.push({\n file: destPath,\n installed: deps.version,\n found: existingVersion,\n });\n logger.warn(\n `stale: ${destPath} is v${existingVersion}; installed @gigamusic/links is v${deps.version}`,\n );\n }\n }\n\n return { ok: stale.length === 0 && missing.length === 0, stale, missing };\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { fragmentsDir } from \"./paths\";\nimport { doctorFragments, syncFragments } from \"./sync\";\n\ninterface ParsedArgs {\n command: \"sync\" | \"doctor\" | \"help\" | \"version\";\n target?: string;\n dryRun: boolean;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const [command = \"help\", ...rest] = argv;\n const parsed: ParsedArgs = {\n command: ([\"sync\", \"doctor\", \"help\", \"version\"].includes(command)\n ? command\n : \"help\") as ParsedArgs[\"command\"],\n dryRun: false,\n };\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i];\n if (arg === \"--dry-run\") parsed.dryRun = true;\n else if (arg === \"--target\") parsed.target = rest[++i];\n else if (arg?.startsWith(\"--target=\")) parsed.target = arg.slice(\"--target=\".length);\n }\n return parsed;\n}\n\nfunction readPackageVersion(metaUrl: string): string {\n try {\n const pkgPath = resolve(fragmentsDir(metaUrl), \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\nfunction printHelp() {\n console.log(`gigamusic-links — Prisma fragment sync for @gigamusic/links\n\nUsage:\n gigamusic-links sync [--target <prismaSchemaFolder>] [--dry-run]\n gigamusic-links doctor [--target <prismaSchemaFolder>]\n gigamusic-links version\n gigamusic-links help\n\nFragments are written with a \"20-\" prefix so they sort AFTER @gigamusic/db's\n\"10-\" fragments and BEFORE consumer-owned \"99-\" files. Default --target is\n./prisma/schema (Prisma 6 prismaSchemaFolder layout).\n`);\n}\n\nexport async function main(argv: string[], metaUrl: string): Promise<number> {\n const args = parseArgs(argv);\n const sourceDir = fragmentsDir(metaUrl);\n const version = readPackageVersion(metaUrl);\n\n if (args.command === \"help\") {\n printHelp();\n return 0;\n }\n if (args.command === \"version\") {\n console.log(version);\n return 0;\n }\n if (args.command === \"sync\") {\n syncFragments(\n { target: args.target, dryRun: args.dryRun },\n { sourceDir, version },\n );\n return 0;\n }\n if (args.command === \"doctor\") {\n const result = doctorFragments({ target: args.target }, { sourceDir, version });\n if (!result.ok) {\n console.error(\n `gigamusic-links doctor: ${result.stale.length} stale, ${result.missing.length} missing`,\n );\n return 1;\n }\n console.log(\"gigamusic-links doctor: all fragments are up to date.\");\n return 0;\n }\n printHelp();\n return 1;\n}\n\nmain(process.argv.slice(2), import.meta.url).then(\n (code) => process.exit(code),\n (err) => {\n console.error(err);\n process.exit(1);\n },\n);\n"]}
|
package/src/cli/index.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { fragmentsDir } from "./paths";
|
|
4
|
-
import { doctorFragments, syncFragments } from "./sync";
|
|
5
|
-
|
|
6
|
-
interface ParsedArgs {
|
|
7
|
-
command: "sync" | "doctor" | "help" | "version";
|
|
8
|
-
target?: string;
|
|
9
|
-
dryRun: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function parseArgs(argv: string[]): ParsedArgs {
|
|
13
|
-
const [command = "help", ...rest] = argv;
|
|
14
|
-
const parsed: ParsedArgs = {
|
|
15
|
-
command: (["sync", "doctor", "help", "version"].includes(command)
|
|
16
|
-
? command
|
|
17
|
-
: "help") as ParsedArgs["command"],
|
|
18
|
-
dryRun: false,
|
|
19
|
-
};
|
|
20
|
-
for (let i = 0; i < rest.length; i++) {
|
|
21
|
-
const arg = rest[i];
|
|
22
|
-
if (arg === "--dry-run") parsed.dryRun = true;
|
|
23
|
-
else if (arg === "--target") parsed.target = rest[++i];
|
|
24
|
-
else if (arg?.startsWith("--target=")) parsed.target = arg.slice("--target=".length);
|
|
25
|
-
}
|
|
26
|
-
return parsed;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function readPackageVersion(metaUrl: string): string {
|
|
30
|
-
try {
|
|
31
|
-
const pkgPath = resolve(fragmentsDir(metaUrl), "..", "package.json");
|
|
32
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { version?: string };
|
|
33
|
-
return pkg.version ?? "0.0.0";
|
|
34
|
-
} catch {
|
|
35
|
-
return "0.0.0";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function printHelp() {
|
|
40
|
-
console.log(`gigamusic-links — Prisma fragment sync for @gigamusic/links
|
|
41
|
-
|
|
42
|
-
Usage:
|
|
43
|
-
gigamusic-links sync [--target <prismaSchemaFolder>] [--dry-run]
|
|
44
|
-
gigamusic-links doctor [--target <prismaSchemaFolder>]
|
|
45
|
-
gigamusic-links version
|
|
46
|
-
gigamusic-links help
|
|
47
|
-
|
|
48
|
-
Fragments are written with a "20-" prefix so they sort AFTER @gigamusic/db's
|
|
49
|
-
"10-" fragments and BEFORE consumer-owned "99-" files. Default --target is
|
|
50
|
-
./prisma/schema (Prisma 6 prismaSchemaFolder layout).
|
|
51
|
-
`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function main(argv: string[], metaUrl: string): Promise<number> {
|
|
55
|
-
const args = parseArgs(argv);
|
|
56
|
-
const sourceDir = fragmentsDir(metaUrl);
|
|
57
|
-
const version = readPackageVersion(metaUrl);
|
|
58
|
-
|
|
59
|
-
if (args.command === "help") {
|
|
60
|
-
printHelp();
|
|
61
|
-
return 0;
|
|
62
|
-
}
|
|
63
|
-
if (args.command === "version") {
|
|
64
|
-
console.log(version);
|
|
65
|
-
return 0;
|
|
66
|
-
}
|
|
67
|
-
if (args.command === "sync") {
|
|
68
|
-
syncFragments(
|
|
69
|
-
{ target: args.target, dryRun: args.dryRun },
|
|
70
|
-
{ sourceDir, version },
|
|
71
|
-
);
|
|
72
|
-
return 0;
|
|
73
|
-
}
|
|
74
|
-
if (args.command === "doctor") {
|
|
75
|
-
const result = doctorFragments({ target: args.target }, { sourceDir, version });
|
|
76
|
-
if (!result.ok) {
|
|
77
|
-
console.error(
|
|
78
|
-
`gigamusic-links doctor: ${result.stale.length} stale, ${result.missing.length} missing`,
|
|
79
|
-
);
|
|
80
|
-
return 1;
|
|
81
|
-
}
|
|
82
|
-
console.log("gigamusic-links doctor: all fragments are up to date.");
|
|
83
|
-
return 0;
|
|
84
|
-
}
|
|
85
|
-
printHelp();
|
|
86
|
-
return 1;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
main(process.argv.slice(2), import.meta.url).then(
|
|
90
|
-
(code) => process.exit(code),
|
|
91
|
-
(err) => {
|
|
92
|
-
console.error(err);
|
|
93
|
-
process.exit(1);
|
|
94
|
-
},
|
|
95
|
-
);
|
package/src/cli/paths.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Locate the shipped `prisma/` fragments directory. At runtime the CLI bundle
|
|
6
|
-
* lives at `<pkg>/dist/cli.js`; the fragments live at `<pkg>/prisma/`.
|
|
7
|
-
*/
|
|
8
|
-
export function fragmentsDir(metaUrl: string): string {
|
|
9
|
-
const here = dirname(fileURLToPath(metaUrl));
|
|
10
|
-
return resolve(here, "..", "prisma");
|
|
11
|
-
}
|