@checkstack/scripts 0.0.2 → 0.1.1

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 CHANGED
@@ -1,5 +1,81 @@
1
1
  # @checkstack/scripts
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 0b9fc58: Fix workspace:\* protocol resolution in published packages
8
+
9
+ Published packages now correctly have resolved dependency versions instead of `workspace:*` references. This is achieved by using `bun publish` which properly resolves workspace protocol references.
10
+
11
+ ## 0.1.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
16
+
17
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
18
+
19
+ ## Changes
20
+
21
+ ### Core Infrastructure (`@checkstack/common`)
22
+
23
+ - Introduced `AccessRule` interface as the primary access control type
24
+ - Added `accessPair()` helper for creating read/manage access rule pairs
25
+ - Added `access()` builder for individual access rules
26
+ - Replaced `Permission` type with `AccessRule` throughout
27
+
28
+ ### API Changes
29
+
30
+ - `env.registerPermissions()` → `env.registerAccessRules()`
31
+ - `meta.permissions` → `meta.access` in RPC contracts
32
+ - `usePermission()` → `useAccess()` in frontend hooks
33
+ - Route `permission:` field → `accessRule:` field
34
+
35
+ ### UI Changes
36
+
37
+ - "Roles & Permissions" tab → "Roles & Access Rules"
38
+ - "You don't have permission..." → "You don't have access..."
39
+ - All permission-related UI text updated
40
+
41
+ ### Documentation & Templates
42
+
43
+ - Updated 18 documentation files with AccessRule terminology
44
+ - Updated 7 scaffolding templates with `accessPair()` pattern
45
+ - All code examples use new AccessRule API
46
+
47
+ ## Migration Guide
48
+
49
+ ### Backend Plugins
50
+
51
+ ```diff
52
+ - import { permissionList } from "./permissions";
53
+ - env.registerPermissions(permissionList);
54
+ + import { accessRules } from "./access";
55
+ + env.registerAccessRules(accessRules);
56
+ ```
57
+
58
+ ### RPC Contracts
59
+
60
+ ```diff
61
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
62
+ + .meta({ userType: "user", access: [access.read] })
63
+ ```
64
+
65
+ ### Frontend Hooks
66
+
67
+ ```diff
68
+ - const canRead = accessApi.usePermission(permissions.read.id);
69
+ + const canRead = accessApi.useAccess(access.read);
70
+ ```
71
+
72
+ ### Routes
73
+
74
+ ```diff
75
+ - permission: permissions.entityRead.id,
76
+ + accessRule: access.read,
77
+ ```
78
+
3
79
  ## 0.0.2
4
80
 
5
81
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/scripts",
3
- "version": "0.0.2",
3
+ "version": "0.1.1",
4
4
  "bin": {
5
5
  "checkstack-scripts": "./src/cli.ts"
6
6
  },
@@ -13,7 +13,7 @@
13
13
  "handlebars": "^4.7.8"
14
14
  },
15
15
  "devDependencies": {
16
- "@checkstack/tsconfig": "workspace:*",
16
+ "@checkstack/tsconfig": "0.0.2",
17
17
  "@types/inquirer": "^8.2.10",
18
18
  "@types/handlebars": "^4.1.0",
19
19
  "typescript": "^5.0.0"
@@ -240,7 +240,7 @@ export async function createCommand() {
240
240
  break;
241
241
  }
242
242
  case "common": {
243
- console.log(` 3. Define your permissions in src/permissions.ts`);
243
+ console.log(` 3. Define your access rules in src/access.ts`);
244
244
  console.log(` 4. Define your schemas in src/schemas.ts`);
245
245
  console.log(` 5. Define your contract in src/rpc-contract.ts`);
246
246
  break;
@@ -3,7 +3,7 @@ import {
3
3
  coreServices,
4
4
  } from "@checkstack/backend-api";
5
5
  import {
6
- permissionList,
6
+ {{pluginNameCamel}}AccessRules,
7
7
  pluginMetadata,
8
8
  {{pluginNameCamel}}Contract,
9
9
  } from "@checkstack/{{pluginBaseName}}-common";
@@ -13,7 +13,7 @@ import { create{{pluginNamePascal}}Router } from "./router";
13
13
  export default createBackendPlugin({
14
14
  metadata: pluginMetadata,
15
15
  register(env) {
16
- env.registerPermissions(permissionList);
16
+ env.registerAccessRules({{pluginNameCamel}}AccessRules);
17
17
 
18
18
  env.registerInit({
19
19
  schema,
@@ -1,15 +1,14 @@
1
1
  import { implement } from "@orpc/server";
2
- import { autoAuthMiddleware, type RpcContext } from "@checkstack/backend-api";
2
+ import { autoAuthMiddleware, type RpcContext, type SafeDatabase } from "@checkstack/backend-api";
3
3
  import { {{pluginNameCamel}}Contract } from "@checkstack/{{pluginBaseName}}-common";
4
- import type { NodePgDatabase } from "drizzle-orm/node-postgres";
5
4
  import type * as schema from "./schema";
6
5
  import { {{pluginNamePascal}}Service } from "./service";
7
6
 
8
7
  /**
9
8
  * Creates the {{pluginBaseName}} router using contract-based implementation.
10
9
  *
11
- * Auth and permissions are automatically enforced via autoAuthMiddleware
12
- * based on the contract's meta.userType and meta.permissions.
10
+ * Auth and access rules are automatically enforced via autoAuthMiddleware
11
+ * based on the contract's meta.userType and meta.access.
13
12
  */
14
13
  const os = implement({{pluginNameCamel}}Contract)
15
14
  .$context<RpcContext>()
@@ -18,7 +17,7 @@ const os = implement({{pluginNameCamel}}Contract)
18
17
  export function create{{pluginNamePascal}}Router({
19
18
  database,
20
19
  }: {
21
- database: NodePgDatabase<typeof schema>;
20
+ database: SafeDatabase<typeof schema>;
22
21
  }) {
23
22
  const service = new {{pluginNamePascal}}Service(database);
24
23
 
@@ -28,7 +27,7 @@ export function create{{pluginNamePascal}}Router({
28
27
  }),
29
28
 
30
29
  getItem: os.getItem.handler(async ({ input }) => {
31
- return await service.getItem(input);
30
+ return await service.getItem(input.id);
32
31
  }),
33
32
 
34
33
  createItem: os.createItem.handler(async ({ input }) => {
@@ -40,7 +39,7 @@ export function create{{pluginNamePascal}}Router({
40
39
  }),
41
40
 
42
41
  deleteItem: os.deleteItem.handler(async ({ input }) => {
43
- await service.deleteItem(input);
42
+ await service.deleteItem(input.id);
44
43
  }),
45
44
  });
46
45
  }
@@ -1,4 +1,4 @@
1
- import type { NodePgDatabase } from "drizzle-orm/node-postgres";
1
+ import type { SafeDatabase } from "@checkstack/backend-api";
2
2
  import { eq } from "drizzle-orm";
3
3
  import * as schema from "./schema";
4
4
  import { {{pluginNameCamel}}Items } from "./schema";
@@ -8,7 +8,7 @@ import type {
8
8
  } from "@checkstack/{{pluginBaseName}}-common";
9
9
 
10
10
  export class {{pluginNamePascal}}Service {
11
- constructor(private readonly database: NodePgDatabase<typeof schema>) {}
11
+ constructor(private readonly database: SafeDatabase<typeof schema>) {}
12
12
 
13
13
  async getItems() {
14
14
  return await this.database.select().from({{pluginNameCamel}}Items);
@@ -2,8 +2,8 @@
2
2
  {{pluginNamePascal}}
3
3
  Common Common package for the
4
4
  {{pluginNamePascal}}
5
- plugin. Contains shared contracts, types, and permissions. ## Structure -
6
- `src/permissions.ts` - Permission definitions - `src/schemas.ts` - Zod schemas
5
+ plugin. Contains shared contracts, types, and access rules. ## Structure -
6
+ `src/access.ts - Access rule definitions - `src/schemas.ts` - Zod schemas
7
7
  and type definitions - `src/rpc-contract.ts` - oRPC contract definition -
8
8
  `src/index.ts` - Barrel exports ## Usage This package is consumed by both: -
9
9
  `@checkstack/{{pluginBaseName}}-backend` - Implements the contract -
@@ -0,0 +1,27 @@
1
+ import { accessPair } from "@checkstack/common";
2
+
3
+ /**
4
+ * Access rules for the {{pluginBaseName}} plugin.
5
+ * Uses accessPair() to create read/manage access rule pairs.
6
+ */
7
+ export const {{pluginNameCamel}}Access = {
8
+ /**
9
+ * Access rules for {{pluginBaseName}} data operations.
10
+ * - read: View {{pluginBaseName}} data (auto-assigned to authenticated users)
11
+ * - manage: Create, update, and delete {{pluginBaseName}} data
12
+ */
13
+ ...accessPair("{{pluginBaseName}}", {
14
+ read: {
15
+ description: "Read {{pluginBaseName}} data",
16
+ isDefault: true, // read is auto-assigned to "users" role
17
+ },
18
+ manage: {
19
+ description: "Manage {{pluginBaseName}} data",
20
+ },
21
+ }),
22
+ };
23
+
24
+ /**
25
+ * List of all access rules for registration.
26
+ */
27
+ export const {{pluginNameCamel}}AccessRules = Object.values({{pluginNameCamel}}Access);
@@ -1,5 +1,5 @@
1
- // Export permissions
2
- export { permissions, permissionList } from "./permissions";
1
+ // Export access rules
2
+ export { {{pluginNameCamel}}Access, {{pluginNameCamel}}AccessRules } from "./access";
3
3
 
4
4
  // Export routes
5
5
  export { {{pluginNameCamel}}Routes } from "./routes";
@@ -1,39 +1,45 @@
1
- import { oc } from "@orpc/contract";
2
- import { createClientDefinition, type ProcedureMetadata } from "@checkstack/common";
1
+ import { createClientDefinition, proc } from "@checkstack/common";
3
2
  import { z } from "zod";
4
3
  import {
5
4
  {{pluginNamePascal}}ItemSchema,
6
5
  Create{{pluginNamePascal}}ItemSchema,
7
6
  Update{{pluginNamePascal}}ItemSchema,
8
7
  } from "./schemas";
9
- import { permissions } from "./permissions";
8
+ import { {{pluginNameCamel}}Access } from "./access";
10
9
  import { pluginMetadata } from "./plugin-metadata";
11
10
 
12
- // Create base builder with ProcedureMetadata support
13
- // See: https://docs.checkstack.dev/common-plugins#contract-based-auth-enforcement
14
- const _base = oc.$meta<ProcedureMetadata>({});
15
-
16
11
  export const {{pluginNameCamel}}Contract = {
17
- // List all items - requires authenticated user with read permission
18
- getItems: _base
19
- .meta({ userType: "user", permissions: [permissions.{{pluginNameCamel}}Read.id] })
20
- .output(z.array({{pluginNamePascal}}ItemSchema)),
12
+ // List all items - requires authenticated user with read access
13
+ getItems: proc({
14
+ operationType: "query",
15
+ userType: "user",
16
+ access: [{{pluginNameCamel}}Access.read],
17
+ }).output(z.array({{pluginNamePascal}}ItemSchema)),
21
18
 
22
19
  // Get single item
23
- getItem: _base
24
- .meta({ userType: "user", permissions: [permissions.{{pluginNameCamel}}Read.id] })
25
- .input(z.string())
20
+ getItem: proc({
21
+ operationType: "query",
22
+ userType: "user",
23
+ access: [{{pluginNameCamel}}Access.read],
24
+ })
25
+ .input(z.object({ id: z.string() }))
26
26
  .output({{pluginNamePascal}}ItemSchema),
27
27
 
28
28
  // Create item
29
- createItem: _base
30
- .meta({ userType: "user", permissions: [permissions.{{pluginNameCamel}}Manage.id] })
29
+ createItem: proc({
30
+ operationType: "mutation",
31
+ userType: "user",
32
+ access: [{{pluginNameCamel}}Access.manage],
33
+ })
31
34
  .input(Create{{pluginNamePascal}}ItemSchema)
32
35
  .output({{pluginNamePascal}}ItemSchema),
33
36
 
34
37
  // Update item
35
- updateItem: _base
36
- .meta({ userType: "user", permissions: [permissions.{{pluginNameCamel}}Manage.id] })
38
+ updateItem: proc({
39
+ operationType: "mutation",
40
+ userType: "user",
41
+ access: [{{pluginNameCamel}}Access.manage],
42
+ })
37
43
  .input(
38
44
  z.object({
39
45
  id: z.string(),
@@ -43,17 +49,21 @@ export const {{pluginNameCamel}}Contract = {
43
49
  .output({{pluginNamePascal}}ItemSchema),
44
50
 
45
51
  // Delete item
46
- deleteItem: _base
47
- .meta({ userType: "user", permissions: [permissions.{{pluginNameCamel}}Manage.id] })
48
- .input(z.string())
52
+ deleteItem: proc({
53
+ operationType: "mutation",
54
+ userType: "user",
55
+ access: [{{pluginNameCamel}}Access.manage],
56
+ })
57
+ .input(z.object({ id: z.string() }))
49
58
  .output(z.void()),
50
59
  };
51
60
 
52
61
  // Export contract type
53
62
  export type {{pluginNamePascal}}Contract = typeof {{pluginNameCamel}}Contract;
54
63
 
55
- // Export client definition for type-safe forPlugin usage
56
- // Use: const client = rpcApi.forPlugin({{pluginNamePascal}}Api);
64
+ // Export client definition for type-safe usePluginClient usage
65
+ // Frontend: const client = usePluginClient({{pluginNamePascal}}Api);
66
+ // Backend: const client = rpcClient.forPlugin({{pluginNamePascal}}Api);
57
67
  export const {{pluginNamePascal}}Api = createClientDefinition(
58
68
  {{pluginNameCamel}}Contract,
59
69
  pluginMetadata
@@ -1,15 +1,9 @@
1
- import { createApiRef } from "@checkstack/frontend-api";
2
- import type { InferClient } from "@checkstack/common";
3
- import { {{pluginNamePascal}}Api } from "@checkstack/{{pluginBaseName}}-common";
4
-
5
- // Re-export types for convenience
1
+ // Re-export types from common for convenience
6
2
  export type {
7
3
  {{pluginNamePascal}}Item,
8
4
  Create{{pluginNamePascal}}Item,
9
5
  Update{{pluginNamePascal}}Item,
10
6
  } from "@checkstack/{{pluginBaseName}}-common";
11
7
 
12
- // {{pluginNamePascal}}ApiClient is the client type inferred from the Api definition
13
- export type {{pluginNamePascal}}ApiClient = InferClient<typeof {{pluginNamePascal}}Api>;
14
-
15
- export const {{pluginNameCamel}}ApiRef = createApiRef<{{pluginNamePascal}}ApiClient>("{{pluginBaseName}}-api");
8
+ // Re-export the Api definition for usePluginClient usage
9
+ export { {{pluginNamePascal}}Api } from "@checkstack/{{pluginBaseName}}-common";
@@ -1,53 +1,55 @@
1
- import { useEffect, useState } from "react";
2
- import { useApi } from "@checkstack/frontend-api";
3
- import { {{pluginNameCamel}}ApiRef, type {{pluginNamePascal}}Item } from "../api";
4
- import { Button, Card } from "@checkstack/ui";
1
+ import { usePluginClient } from "@checkstack/frontend-api";
2
+ import { {{pluginNamePascal}}Api } from "@checkstack/{{pluginBaseName}}-common";
3
+ import type { {{pluginNamePascal}}Item } from "../api";
4
+ import { Button, Card, PageLayout } from "@checkstack/ui";
5
5
  import { useNavigate } from "react-router-dom";
6
+ import { Boxes } from "lucide-react";
6
7
 
7
8
  export const {{pluginNamePascal}}ListPage = () => {
8
- const api = useApi({{pluginNameCamel}}ApiRef);
9
+ const client = usePluginClient({{pluginNamePascal}}Api);
9
10
  const navigate = useNavigate();
10
- const [items, setItems] = useState<{{pluginNamePascal}}Item[]>([]);
11
- const [loading, setLoading] = useState(true);
12
- const [error, setError] = useState<Error>();
13
11
 
14
- useEffect(() => {
15
- api
16
- .getItems()
17
- .then(setItems)
18
- .catch(setError)
19
- .finally(() => setLoading(false));
20
- }, [api]);
12
+ // Query for items - automatic loading and error states
13
+ const { data: items = [], isLoading, error, refetch } = client.getItems.useQuery({});
21
14
 
22
- const handleDelete = async (id: string) => {
23
- try {
24
- await api.deleteItem(id);
25
- setItems(items.filter((item) => item.id !== id));
26
- } catch (error) {
27
- console.error("Failed to delete item:", error);
28
- }
15
+ // Delete mutation with refetch on success
16
+ const deleteMutation = client.deleteItem.useMutation({
17
+ onSuccess: () => {
18
+ void refetch();
19
+ },
20
+ });
21
+
22
+ const handleDelete = (id: string) => {
23
+ deleteMutation.mutate({ id });
29
24
  };
30
25
 
31
- if (loading) return <div className="p-6">Loading...</div>;
32
- if (error) return <div className="p-6 text-red-500">Error: {error.message}</div>;
26
+ if (error) {
27
+ return (
28
+ <PageLayout title="{{pluginNamePascal}}" icon={Boxes}>
29
+ <div className="text-destructive">Error: {error.message}</div>
30
+ </PageLayout>
31
+ );
32
+ }
33
33
 
34
34
  return (
35
- <div className="p-6">
36
- <div className="flex justify-between items-center mb-4">
37
- <h1 className="text-2xl font-bold">{{pluginNamePascal}} Items</h1>
35
+ <PageLayout
36
+ title="{{pluginNamePascal}}"
37
+ icon={Boxes}
38
+ loading={isLoading}
39
+ actions={
38
40
  <Button onClick={() => navigate("/{{pluginBaseName}}/new")}>
39
41
  Create Item
40
42
  </Button>
41
- </div>
42
-
43
+ }
44
+ >
43
45
  <div className="grid gap-4">
44
- {items.map((item) => (
46
+ {items.map((item: {{pluginNamePascal}}Item) => (
45
47
  <Card key={item.id} className="p-4">
46
48
  <div className="flex justify-between items-start">
47
49
  <div>
48
50
  <h3 className="font-semibold">{item.name}</h3>
49
51
  {item.description && (
50
- <p className="text-sm text-gray-600">{item.description}</p>
52
+ <p className="text-sm text-muted-foreground">{item.description}</p>
51
53
  )}
52
54
  </div>
53
55
  <div className="flex gap-2">
@@ -62,8 +64,9 @@ export const {{pluginNamePascal}}ListPage = () => {
62
64
  variant="destructive"
63
65
  size="sm"
64
66
  onClick={() => handleDelete(item.id)}
67
+ disabled={deleteMutation.isPending}
65
68
  >
66
- Delete
69
+ {deleteMutation.isPending ? "Deleting..." : "Delete"}
67
70
  </Button>
68
71
  </div>
69
72
  </div>
@@ -71,11 +74,11 @@ export const {{pluginNamePascal}}ListPage = () => {
71
74
  ))}
72
75
 
73
76
  {items.length === 0 && (
74
- <Card className="p-8 text-center text-gray-500">
77
+ <Card className="p-8 text-center text-muted-foreground">
75
78
  No items yet. Create your first item to get started.
76
79
  </Card>
77
80
  )}
78
81
  </div>
79
- </div>
82
+ </PageLayout>
80
83
  );
81
84
  };
@@ -1,7 +1,6 @@
1
- import { createFrontendPlugin, rpcApiRef, type ApiRef } from "@checkstack/frontend-api";
2
- import { {{pluginNameCamel}}ApiRef, type {{pluginNamePascal}}ApiClient } from "./api";
1
+ import { createFrontendPlugin } from "@checkstack/frontend-api";
3
2
  import { {{pluginNamePascal}}ListPage } from "./components/{{pluginNamePascal}}ListPage";
4
- import { {{pluginNameCamel}}Routes, {{pluginNamePascal}}Api, pluginMetadata, permissions } from "@checkstack/{{pluginBaseName}}-common";
3
+ import { {{pluginNameCamel}}Routes, pluginMetadata, {{pluginNameCamel}}Access } from "@checkstack/{{pluginBaseName}}-common";
5
4
 
6
5
  export default createFrontendPlugin({
7
6
  metadata: pluginMetadata,
@@ -12,22 +11,9 @@ export default createFrontendPlugin({
12
11
  route: {{pluginNameCamel}}Routes.routes.home,
13
12
  element: <{{pluginNamePascal}}ListPage />,
14
13
  title: "{{pluginNamePascal}}",
15
- permission: permissions.{{pluginNameCamel}}Read,
16
- },
17
- ],
18
-
19
- // Register client API using oRPC
20
- apis: [
21
- {
22
- ref: {{pluginNameCamel}}ApiRef,
23
- factory: (deps: { get: <T>(ref: ApiRef<T>) => T }): {{pluginNamePascal}}ApiClient => {
24
- const rpcApi = deps.get(rpcApiRef);
25
- // Create type-safe client using the plugin's Api definition
26
- return rpcApi.forPlugin({{pluginNamePascal}}Api);
27
- },
14
+ accessRule: {{pluginNameCamel}}Access.read,
28
15
  },
29
16
  ],
30
17
  });
31
18
 
32
19
  export * from "./api";
33
-
@@ -1,17 +0,0 @@
1
- import { createPermission } from "@checkstack/common";
2
-
3
- export const permissions = {
4
- {{pluginNameCamel}}Read: createPermission(
5
- "{{pluginBaseName}}",
6
- "read",
7
- "Read {{pluginBaseName}} data",
8
- { isAuthenticatedDefault: true } // Auto-assigned to "users" role
9
- ),
10
- {{pluginNameCamel}}Manage: createPermission(
11
- "{{pluginBaseName}}",
12
- "manage",
13
- "Manage {{pluginBaseName}} data"
14
- ),
15
- };
16
-
17
- export const permissionList = Object.values(permissions);