@checkstack/scripts 0.3.3 → 0.4.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/package.json +15 -5
- package/src/commands/create.ts +16 -23
- package/src/commands/plugin-pack.ts +17 -28
- package/src/dev-tui/App.render.test.tsx +135 -0
- package/src/dev-tui/App.smoke.test.tsx +142 -0
- package/src/dev-tui/App.tsx +522 -0
- package/src/dev-tui/alert-buffer.test.ts +62 -0
- package/src/dev-tui/alert-buffer.ts +51 -0
- package/src/dev-tui/alt-screen.test.ts +66 -0
- package/src/dev-tui/alt-screen.ts +65 -0
- package/src/dev-tui/cli.tsx +89 -0
- package/src/dev-tui/fake-supervisor.ts +76 -0
- package/src/dev-tui/graceful-shutdown.test.ts +61 -0
- package/src/dev-tui/graceful-shutdown.ts +32 -0
- package/src/dev-tui/kill-tree.test.ts +47 -0
- package/src/dev-tui/kill-tree.ts +64 -0
- package/src/dev-tui/layout.test.ts +89 -0
- package/src/dev-tui/layout.ts +126 -0
- package/src/dev-tui/log-level.test.ts +94 -0
- package/src/dev-tui/log-level.ts +104 -0
- package/src/dev-tui/plain-runner.ts +60 -0
- package/src/dev-tui/process-config.test.ts +42 -0
- package/src/dev-tui/process-config.ts +61 -0
- package/src/dev-tui/readiness.test.ts +54 -0
- package/src/dev-tui/readiness.ts +44 -0
- package/src/dev-tui/scrollback.test.ts +83 -0
- package/src/dev-tui/scrollback.ts +82 -0
- package/src/dev-tui/supervisor.ts +231 -0
- package/src/dev-tui/text.test.ts +72 -0
- package/src/dev-tui/text.ts +101 -0
- package/src/dev-tui/types.ts +29 -0
- package/src/scaffold/index.ts +22 -0
- package/src/scaffold/resolve-versions.test.ts +49 -0
- package/src/scaffold/resolve-versions.ts +55 -0
- package/src/scaffold/rewrite-workspace-versions.test.ts +102 -0
- package/src/scaffold/rewrite-workspace-versions.ts +111 -0
- package/src/scaffold/scaffold-plugin.test.ts +209 -0
- package/src/scaffold/scaffold-plugin.ts +309 -0
- package/src/templates/backend/.changeset/initial.md.hbs +1 -1
- package/src/templates/backend/drizzle/0000_init.sql +7 -0
- package/src/templates/backend/drizzle/meta/0000_snapshot.json +65 -0
- package/src/templates/backend/drizzle/meta/_journal.json +13 -0
- package/src/templates/backend/drizzle.config.ts.hbs +5 -1
- package/src/templates/backend/package.json.hbs +7 -3
- package/src/templates/backend/src/index.ts.hbs +1 -1
- package/src/templates/backend/src/router.ts.hbs +1 -1
- package/src/templates/backend/src/service.ts.hbs +1 -1
- package/src/templates/common/.changeset/initial.md.hbs +1 -1
- package/src/templates/common/README.md.hbs +28 -11
- package/src/templates/common/package.json.hbs +1 -1
- package/src/templates/common/src/plugin-metadata.ts.hbs +1 -1
- package/src/templates/frontend/.changeset/initial.md.hbs +1 -1
- package/src/templates/frontend/package.json.hbs +2 -2
- package/src/templates/frontend/src/api.ts.hbs +2 -2
- package/src/templates/frontend/src/components/{{pluginNamePascal}}ListPage.tsx.hbs +1 -1
- package/src/templates/frontend/src/index.tsx.hbs +10 -4
- package/src/templates/standalone-root/.changeset/config.json.hbs +11 -0
- package/src/templates/standalone-root/.changeset/initial.md.hbs +9 -0
- package/src/templates/standalone-root/README.md.hbs +75 -0
- package/src/templates/standalone-root/eslint.config.mjs.hbs +37 -0
- package/src/templates/standalone-root/package.json.hbs +27 -0
- package/src/templates/standalone-root/tsconfig.json.hbs +13 -0
- package/src/templates.test.ts +20 -0
- package/src/tui/components.test.tsx +28 -0
- package/src/tui/components.tsx +159 -0
- package/src/tui/index.ts +31 -0
- package/src/tui/theme.test.ts +54 -0
- package/src/tui/theme.ts +60 -0
- package/src/utils/template.ts +42 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "00000000-0000-0000-0000-000000000001",
|
|
3
|
+
"prevId": "00000000-0000-0000-0000-000000000000",
|
|
4
|
+
"version": "7",
|
|
5
|
+
"dialect": "postgresql",
|
|
6
|
+
"tables": {
|
|
7
|
+
"public.items": {
|
|
8
|
+
"name": "items",
|
|
9
|
+
"schema": "",
|
|
10
|
+
"columns": {
|
|
11
|
+
"id": {
|
|
12
|
+
"name": "id",
|
|
13
|
+
"type": "uuid",
|
|
14
|
+
"primaryKey": true,
|
|
15
|
+
"notNull": true,
|
|
16
|
+
"default": "gen_random_uuid()"
|
|
17
|
+
},
|
|
18
|
+
"name": {
|
|
19
|
+
"name": "name",
|
|
20
|
+
"type": "text",
|
|
21
|
+
"primaryKey": false,
|
|
22
|
+
"notNull": true
|
|
23
|
+
},
|
|
24
|
+
"description": {
|
|
25
|
+
"name": "description",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": false
|
|
29
|
+
},
|
|
30
|
+
"created_at": {
|
|
31
|
+
"name": "created_at",
|
|
32
|
+
"type": "timestamp",
|
|
33
|
+
"primaryKey": false,
|
|
34
|
+
"notNull": true,
|
|
35
|
+
"default": "now()"
|
|
36
|
+
},
|
|
37
|
+
"updated_at": {
|
|
38
|
+
"name": "updated_at",
|
|
39
|
+
"type": "timestamp",
|
|
40
|
+
"primaryKey": false,
|
|
41
|
+
"notNull": true,
|
|
42
|
+
"default": "now()"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"indexes": {},
|
|
46
|
+
"foreignKeys": {},
|
|
47
|
+
"compositePrimaryKeys": {},
|
|
48
|
+
"uniqueConstraints": {},
|
|
49
|
+
"policies": {},
|
|
50
|
+
"checkConstraints": {},
|
|
51
|
+
"isRLSEnabled": false
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"enums": {},
|
|
55
|
+
"schemas": {},
|
|
56
|
+
"sequences": {},
|
|
57
|
+
"roles": {},
|
|
58
|
+
"policies": {},
|
|
59
|
+
"views": {},
|
|
60
|
+
"_meta": {
|
|
61
|
+
"columns": {},
|
|
62
|
+
"schemas": {},
|
|
63
|
+
"tables": {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -2,7 +2,11 @@ import { defineConfig } from "drizzle-kit";
|
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
schema: "./src/schema.ts",
|
|
5
|
-
|
|
5
|
+
// The platform's plugin loader applies migrations from `<plugin>/drizzle`,
|
|
6
|
+
// so generated SQL must land there (NOT `./migrations`). The scaffold ships
|
|
7
|
+
// an initial `0000_init` migration for the example `items` table; running
|
|
8
|
+
// `bunx drizzle-kit generate` after editing the schema appends here.
|
|
9
|
+
out: "./drizzle",
|
|
6
10
|
dialect: "postgresql",
|
|
7
11
|
dbCredentials: {
|
|
8
12
|
url: process.env.DATABASE_URL || "postgresql://localhost:5432/checkstack",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "{{scoped pluginName}}",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"description": "{{pluginDescription}}",
|
|
5
5
|
"author": "Checkstack contributors",
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
},
|
|
12
12
|
"checkstack": {
|
|
13
13
|
"type": "backend",
|
|
14
|
-
"pluginId": "{{pluginBaseName}}"
|
|
14
|
+
"pluginId": "{{pluginBaseName}}",
|
|
15
|
+
"bundle": [
|
|
16
|
+
"{{scoped pluginBaseName}}-common",
|
|
17
|
+
"{{scoped pluginBaseName}}-frontend"
|
|
18
|
+
]
|
|
15
19
|
},
|
|
16
20
|
"scripts": {
|
|
17
21
|
"dev": "checkstack-dev",
|
|
@@ -24,7 +28,7 @@
|
|
|
24
28
|
"dependencies": {
|
|
25
29
|
"@checkstack/backend-api": "workspace:*",
|
|
26
30
|
"@checkstack/common": "workspace:*",
|
|
27
|
-
"
|
|
31
|
+
"{{scoped pluginBaseName}}-common": "workspace:*",
|
|
28
32
|
"@orpc/server": "^1.13.2",
|
|
29
33
|
"drizzle-orm": "^0.45.1"
|
|
30
34
|
},
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
{{pluginNameCamel}}AccessRules,
|
|
7
7
|
pluginMetadata,
|
|
8
8
|
{{pluginNameCamel}}Contract,
|
|
9
|
-
} from "
|
|
9
|
+
} from "{{scoped pluginBaseName}}-common";
|
|
10
10
|
import * as schema from "./schema";
|
|
11
11
|
import { create{{pluginNamePascal}}Router } from "./router";
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { implement } from "@orpc/server";
|
|
2
2
|
import { autoAuthMiddleware, correlationMiddleware, type RpcContext, type SafeDatabase } from "@checkstack/backend-api";
|
|
3
|
-
import { {{pluginNameCamel}}Contract } from "
|
|
3
|
+
import { {{pluginNameCamel}}Contract } from "{{scoped pluginBaseName}}-common";
|
|
4
4
|
import type * as schema from "./schema";
|
|
5
5
|
import { {{pluginNamePascal}}Service } from "./service";
|
|
6
6
|
|
|
@@ -5,7 +5,7 @@ import { {{pluginNameCamel}}Items } from "./schema";
|
|
|
5
5
|
import type {
|
|
6
6
|
Create{{pluginNamePascal}}Item,
|
|
7
7
|
Update{{pluginNamePascal}}Item,
|
|
8
|
-
} from "
|
|
8
|
+
} from "{{scoped pluginBaseName}}-common";
|
|
9
9
|
|
|
10
10
|
export class {{pluginNamePascal}}Service {
|
|
11
11
|
constructor(private readonly database: SafeDatabase<typeof schema>) {}
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
Common
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`src/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
# {{pluginNamePascal}} Common
|
|
2
|
+
|
|
3
|
+
Common package for the {{pluginNamePascal}} plugin. Contains shared
|
|
4
|
+
contracts, types, and access rules.
|
|
5
|
+
|
|
6
|
+
## Structure
|
|
7
|
+
|
|
8
|
+
- `src/access.ts` - Access rule definitions
|
|
9
|
+
- `src/schemas.ts` - Zod schemas and type definitions
|
|
10
|
+
- `src/rpc-contract.ts` - oRPC contract definition
|
|
11
|
+
- `src/index.ts` - Barrel exports
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
This package is consumed by both:
|
|
16
|
+
|
|
17
|
+
- `{{scoped pluginBaseName}}-backend` - Implements the contract
|
|
18
|
+
- `{{scoped pluginBaseName}}-frontend` - Consumes the contract
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Type check
|
|
24
|
+
bun run typecheck
|
|
25
|
+
|
|
26
|
+
# Lint
|
|
27
|
+
bun run lint
|
|
28
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "{{scoped pluginName}}",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"description": "{{pluginDescription}}",
|
|
5
5
|
"author": "Checkstack contributors",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@checkstack/frontend-api": "workspace:*",
|
|
26
26
|
"@checkstack/common": "workspace:*",
|
|
27
|
-
"
|
|
27
|
+
"{{scoped pluginBaseName}}-common": "workspace:*",
|
|
28
28
|
"@checkstack/ui": "workspace:*",
|
|
29
29
|
"react": "^18.3.1",
|
|
30
30
|
"react-router-dom": "^7.1.1",
|
|
@@ -3,7 +3,7 @@ export type {
|
|
|
3
3
|
{{pluginNamePascal}}Item,
|
|
4
4
|
Create{{pluginNamePascal}}Item,
|
|
5
5
|
Update{{pluginNamePascal}}Item,
|
|
6
|
-
} from "
|
|
6
|
+
} from "{{scoped pluginBaseName}}-common";
|
|
7
7
|
|
|
8
8
|
// Re-export the Api definition for usePluginClient usage
|
|
9
|
-
export { {{pluginNamePascal}}Api } from "
|
|
9
|
+
export { {{pluginNamePascal}}Api } from "{{scoped pluginBaseName}}-common";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { usePluginClient } from "@checkstack/frontend-api";
|
|
2
|
-
import { {{pluginNamePascal}}Api } from "
|
|
2
|
+
import { {{pluginNamePascal}}Api } from "{{scoped pluginBaseName}}-common";
|
|
3
3
|
import type { {{pluginNamePascal}}Item } from "../api";
|
|
4
4
|
import { Button, Card, PageLayout } from "@checkstack/ui";
|
|
5
5
|
import { useNavigate } from "react-router-dom";
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { createFrontendPlugin } from "@checkstack/frontend-api";
|
|
2
|
-
import { {{
|
|
3
|
-
import { {{pluginNameCamel}}Routes, pluginMetadata, {{pluginNameCamel}}Access } from "@checkstack/{{pluginBaseName}}-common";
|
|
2
|
+
import { {{pluginNameCamel}}Routes, pluginMetadata, {{pluginNameCamel}}Access } from "{{scoped pluginBaseName}}-common";
|
|
4
3
|
|
|
5
4
|
export default createFrontendPlugin({
|
|
6
5
|
metadata: pluginMetadata,
|
|
7
6
|
|
|
8
|
-
// Register routes using typed route definitions
|
|
7
|
+
// Register routes using typed route definitions. Each route declares a `load`
|
|
8
|
+
// thunk: the framework code-splits the page and wraps it in Suspense + a
|
|
9
|
+
// per-plugin error boundary, so the page chunk is fetched on navigation
|
|
10
|
+
// (never in the initial app load). Use `load` for pages; light, always-on
|
|
11
|
+
// slot extensions (navbar/user-menu) use eager `component` instead.
|
|
9
12
|
routes: [
|
|
10
13
|
{
|
|
11
14
|
route: {{pluginNameCamel}}Routes.routes.home,
|
|
12
|
-
|
|
15
|
+
load: () =>
|
|
16
|
+
import("./components/{{pluginNamePascal}}ListPage").then((m) => ({
|
|
17
|
+
default: m.{{pluginNamePascal}}ListPage,
|
|
18
|
+
})),
|
|
13
19
|
title: "{{pluginNamePascal}}",
|
|
14
20
|
accessRule: {{pluginNameCamel}}Access.read,
|
|
15
21
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
|
|
3
|
+
"changelog": "@changesets/cli/changelog",
|
|
4
|
+
"commit": false,
|
|
5
|
+
"fixed": [],
|
|
6
|
+
"linked": [],
|
|
7
|
+
"access": "public",
|
|
8
|
+
"baseBranch": "main",
|
|
9
|
+
"updateInternalDependencies": "patch",
|
|
10
|
+
"ignore": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# {{pluginNamePascal}} plugin
|
|
2
|
+
|
|
3
|
+
{{pluginDescription}}
|
|
4
|
+
|
|
5
|
+
This is a standalone [Checkstack](https://enyineer.github.io/checkstack/)
|
|
6
|
+
plugin workspace generated by `create-checkstack-plugin`. It contains a
|
|
7
|
+
`common` contract package, a `backend` package implementing it, and a
|
|
8
|
+
`frontend` package consuming it.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- [Bun](https://bun.sh) 1.1 or newer.
|
|
13
|
+
- A local Postgres database. The dev server defaults to
|
|
14
|
+
`postgresql://checkstack:checkstack@localhost:5432/checkstack`. A quick
|
|
15
|
+
way to get one:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
docker run --name checkstack-pg -e POSTGRES_USER=checkstack \
|
|
19
|
+
-e POSTGRES_PASSWORD=checkstack -e POSTGRES_DB=checkstack \
|
|
20
|
+
-p 5432:5432 -d postgres:16-alpine
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
No Redis is required: the dev server auto-loads in-memory queue and cache
|
|
24
|
+
providers.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Develop
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bun run dev
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This boots the Checkstack dev server with synthetic dev auth enabled, runs
|
|
39
|
+
the backend with hot reload, and starts the frontend Vite dev server. Every
|
|
40
|
+
access rule the plugin registers is auto-granted to a `dev-user`, so no
|
|
41
|
+
login is needed.
|
|
42
|
+
|
|
43
|
+
Once it is up, the plugin's CRUD API is served under `/api/{{pluginBaseName}}/*`.
|
|
44
|
+
For example, list the example `items`:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
curl -X POST http://localhost:3000/api/{{pluginBaseName}}/getItems \
|
|
48
|
+
-H 'content-type: application/json' \
|
|
49
|
+
-d '{}'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## What is in the box
|
|
53
|
+
|
|
54
|
+
- `packages/{{pluginBaseName}}-common` - the oRPC contract, Zod schemas, and
|
|
55
|
+
access rules shared by backend and frontend.
|
|
56
|
+
- `packages/{{pluginBaseName}}-backend` - a single `items` table (Drizzle)
|
|
57
|
+
plus CRUD procedures (`getItems`, `getItem`, `createItem`, `updateItem`,
|
|
58
|
+
`deleteItem`).
|
|
59
|
+
- `packages/{{pluginBaseName}}-frontend` - one list page wired to the typed
|
|
60
|
+
client.
|
|
61
|
+
|
|
62
|
+
> The default skeleton deliberately omits a reactive `defineEntity`
|
|
63
|
+
> state machine to keep first boot minimal. See the Checkstack docs on
|
|
64
|
+
> reactive entities when you are ready to add one.
|
|
65
|
+
|
|
66
|
+
## Pack for distribution
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bun run pack
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This produces a bundle tarball under
|
|
73
|
+
`packages/{{pluginBaseName}}-backend/dist/` containing the backend primary
|
|
74
|
+
plus the `-common` and `-frontend` siblings, ready to upload via the Plugin
|
|
75
|
+
Manager.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
+
|
|
5
|
+
// Standalone Checkstack plugin lint config. Mirrors the subset of the
|
|
6
|
+
// monorepo's rules that apply to plugin source. The monorepo-only custom
|
|
7
|
+
// `checkstack/*` rules and the project-references plumbing are intentionally
|
|
8
|
+
// omitted - they depend on the monorepo's internal tooling.
|
|
9
|
+
export default tseslint.config(
|
|
10
|
+
{
|
|
11
|
+
ignores: [
|
|
12
|
+
"**/node_modules/**",
|
|
13
|
+
"**/dist/**",
|
|
14
|
+
"**/.tsbuild/**",
|
|
15
|
+
"**/drizzle/**",
|
|
16
|
+
"**/*.test.ts",
|
|
17
|
+
"**/*.test.tsx",
|
|
18
|
+
"**/*.e2e.ts",
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
js.configs.recommended,
|
|
22
|
+
...tseslint.configs.recommended,
|
|
23
|
+
{
|
|
24
|
+
plugins: {
|
|
25
|
+
"react-hooks": reactHooks,
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
29
|
+
"@typescript-eslint/no-unused-vars": [
|
|
30
|
+
"error",
|
|
31
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
32
|
+
],
|
|
33
|
+
"react-hooks/rules-of-hooks": "error",
|
|
34
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{pluginBaseName}}-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "{{pluginDescription}}",
|
|
5
|
+
"private": true,
|
|
6
|
+
"license": "Elastic-2.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"workspaces": [
|
|
9
|
+
"packages/*"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "bun run --filter '{{scoped pluginBaseName}}-backend' dev",
|
|
13
|
+
"pack": "bun run --filter '{{scoped pluginBaseName}}-backend' pack -- --bundle",
|
|
14
|
+
"typecheck": "bun run --filter '*' typecheck",
|
|
15
|
+
"lint": "bun run --filter '*' lint",
|
|
16
|
+
"test": "bun run --filter '*' test",
|
|
17
|
+
"changeset": "changeset"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@changesets/cli": "^2.31.0",
|
|
21
|
+
"@eslint/js": "^9.39.4",
|
|
22
|
+
"eslint": "^9.39.4",
|
|
23
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
24
|
+
"typescript": "^5.7.2",
|
|
25
|
+
"typescript-eslint": "^8.59.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"noEmit": true,
|
|
4
|
+
"composite": false
|
|
5
|
+
},
|
|
6
|
+
"files": [],
|
|
7
|
+
"include": [],
|
|
8
|
+
"references": [
|
|
9
|
+
{ "path": "./packages/{{pluginBaseName}}-common" },
|
|
10
|
+
{ "path": "./packages/{{pluginBaseName}}-backend" },
|
|
11
|
+
{ "path": "./packages/{{pluginBaseName}}-frontend" }
|
|
12
|
+
]
|
|
13
|
+
}
|
package/src/templates.test.ts
CHANGED
|
@@ -200,6 +200,26 @@ describe("CLI Template Scaffolding", () => {
|
|
|
200
200
|
expect(pkg.scripts?.dev).toBe("checkstack-dev");
|
|
201
201
|
}
|
|
202
202
|
});
|
|
203
|
+
|
|
204
|
+
if (pluginType === "common") {
|
|
205
|
+
// Regression guard (#251 Phase 3): the common package's
|
|
206
|
+
// `definePluginMetadata({ pluginId })` MUST be the bare base name —
|
|
207
|
+
// the same value the backend's `checkstack.pluginId` uses for
|
|
208
|
+
// `/api/<pluginId>/*` routing. If this drifts to `<base>-common`
|
|
209
|
+
// (the `pluginName`), the plugin-metadata registry is keyed
|
|
210
|
+
// differently from the route and every request 500s with
|
|
211
|
+
// "Plugin metadata not found in registry". The end-to-end
|
|
212
|
+
// `*.it.test.ts` lane catches it too, but this pins it in the fast
|
|
213
|
+
// lane so the fix can't silently regress.
|
|
214
|
+
it("declares the routing pluginId (base name) in plugin-metadata.ts", () => {
|
|
215
|
+
const metaSrc = readFileSync(
|
|
216
|
+
path.join(targetDir, "src", "plugin-metadata.ts"),
|
|
217
|
+
"utf8",
|
|
218
|
+
);
|
|
219
|
+
expect(metaSrc).toContain(`pluginId:\n"${TEST_BASE_NAME}"`);
|
|
220
|
+
expect(metaSrc).not.toContain(`"${TEST_BASE_NAME}-common"`);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
203
223
|
});
|
|
204
224
|
}
|
|
205
225
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { renderToString } from "ink";
|
|
4
|
+
import { Spinner, SPINNER_FRAMES } from "./components.tsx";
|
|
5
|
+
|
|
6
|
+
describe("Spinner", () => {
|
|
7
|
+
it("renders the glyph for the given frame", () => {
|
|
8
|
+
expect(renderToString(<Spinner frame={0} />)).toContain(SPINNER_FRAMES[0]);
|
|
9
|
+
expect(renderToString(<Spinner frame={1} />)).toContain(SPINNER_FRAMES[1]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("wraps the frame index over the frame set", () => {
|
|
13
|
+
const len = SPINNER_FRAMES.length;
|
|
14
|
+
expect(renderToString(<Spinner frame={len} />)).toContain(
|
|
15
|
+
SPINNER_FRAMES[0],
|
|
16
|
+
);
|
|
17
|
+
expect(renderToString(<Spinner frame={len + 2} />)).toContain(
|
|
18
|
+
SPINNER_FRAMES[2],
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("is safe for a negative frame", () => {
|
|
23
|
+
// Should not throw or render `undefined`.
|
|
24
|
+
const frame = renderToString(<Spinner frame={-1} />);
|
|
25
|
+
expect(frame).toContain(SPINNER_FRAMES[SPINNER_FRAMES.length - 1]);
|
|
26
|
+
expect(frame).not.toContain("undefined");
|
|
27
|
+
});
|
|
28
|
+
});
|