@harperfast/template-react-studio 1.6.3 → 1.6.4

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.
Files changed (25) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +1425 -300
  2. package/.agents/skills/harper-best-practices/SKILL.md +24 -20
  3. package/.agents/skills/harper-best-practices/rules/adding-tables-with-schemas.md +2 -0
  4. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +141 -18
  5. package/.agents/skills/harper-best-practices/rules/caching.md +134 -21
  6. package/.agents/skills/harper-best-practices/rules/checking-authentication.md +139 -148
  7. package/.agents/skills/harper-best-practices/rules/creating-a-fabric-account-and-cluster.md +2 -0
  8. package/.agents/skills/harper-best-practices/rules/creating-harper-apps.md +2 -0
  9. package/.agents/skills/harper-best-practices/rules/custom-resources.md +2 -0
  10. package/.agents/skills/harper-best-practices/rules/defining-relationships.md +2 -0
  11. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +97 -77
  12. package/.agents/skills/harper-best-practices/rules/extending-tables.md +2 -0
  13. package/.agents/skills/harper-best-practices/rules/handling-binary-data.md +2 -0
  14. package/.agents/skills/harper-best-practices/rules/logging.md +154 -77
  15. package/.agents/skills/harper-best-practices/rules/programmatic-table-requests.md +32 -26
  16. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +190 -15
  17. package/.agents/skills/harper-best-practices/rules/real-time-apps.md +80 -21
  18. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +2 -0
  19. package/.agents/skills/harper-best-practices/rules/serving-web-content.md +2 -0
  20. package/.agents/skills/harper-best-practices/rules/typescript-type-stripping.md +2 -0
  21. package/.agents/skills/harper-best-practices/rules/using-blob-datatype.md +2 -0
  22. package/.agents/skills/harper-best-practices/rules/vector-indexing.md +85 -120
  23. package/.agents/skills/harper-best-practices/rules.manifest.yaml +258 -0
  24. package/package.json +1 -1
  25. package/skills-lock.json +1 -1
@@ -1,199 +1,190 @@
1
1
  ---
2
2
  name: checking-authentication
3
3
  description: How to handle user authentication and sessions in Harper Resources.
4
+ metadata:
5
+ mode: generate
6
+ sources:
7
+ - >-
8
+ reference/v5/resources/resource-api.md#`getCurrentUser(): User |
9
+ undefined`
10
+ - reference/v5/resources/resource-api.md#Session and Login from a Resource
11
+ - reference/v5/security/jwt-authentication.md
12
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
13
+ inputHash: fdd9ec3b11011490
4
14
  ---
5
15
 
6
16
  # Checking Authentication
7
17
 
8
- Instructions for the agent to follow when handling authentication and sessions.
18
+ Instructions for the agent to follow when handling user authentication and session management inside Harper Resources.
9
19
 
10
20
  ## When to Use
11
21
 
12
- Use this skill when you need to implement sign-in/sign-out functionality, protect specific resource endpoints, or identify the currently logged-in user in a Harper application.
22
+ Apply this rule when implementing authentication checks, login/logout flows, or token issuance inside a custom Resource. Use it any time a Resource needs to identify the current user, establish a session, or issue JWTs to clients. See [custom-resources.md](custom-resources.md) for the general Resource authoring pattern.
13
23
 
14
24
  ## How It Works
15
25
 
16
- 1. **Configure Harper for Sessions**: Ensure `harper-config.yaml` has sessions enabled and local auto-authorization disabled for testing:
17
- ```yaml
18
- authentication:
19
- authorizeLocal: false
20
- enableSessions: true
21
- ```
22
- 2. **Implement Sign In**: Use `this.getContext().login(username, password)` to create a session:
23
- ```typescript
24
- async post(_target, data) {
25
- const context = this.getContext();
26
- try {
27
- await context.login(data.username, data.password);
28
- } catch {
29
- return new Response('Invalid credentials', { status: 403 });
30
- }
31
- return new Response('Logged in', { status: 200 });
32
- }
33
- ```
34
- 3. **Identify Current User**: Use `this.getCurrentUser()` to access session data:
35
- ```typescript
36
- async get() {
37
- const user = this.getCurrentUser?.();
26
+ 1. **Check the current user** with `getCurrentUser()`. Call it inside any Resource method to retrieve the authenticated user or `undefined` if no user is authenticated. Guard protected endpoints by returning a `401` when the result is `undefined`.
27
+
28
+ ```javascript
29
+ async get(target) {
30
+ const user = this.getCurrentUser();
38
31
  if (!user) return new Response(null, { status: 401 });
39
32
  return { username: user.username, role: user.role };
40
33
  }
41
34
  ```
42
- 4. **Implement Sign Out**: Use `this.getContext().logout()` or delete the session from context:
43
- ```typescript
44
- async post() {
45
- const context = this.getContext();
46
- await context.session?.delete?.(context.session.id);
47
- return new Response('Logged out', { status: 200 });
48
- }
49
- ```
50
- 5. **Protect Routes**: In your Resource, use `allowRead()`, `allowUpdate()`, etc., to enforce authorization logic based on `this.getCurrentUser()`. For privileged actions, verify `user.role.permission.super_user`.
51
-
52
- ## Examples
53
-
54
- ### Sign In Implementation
55
-
56
- ```typescript
57
- async post(_target, data) {
58
- const context = this.getContext();
59
- try {
60
- await context.login(data.username, data.password);
61
- } catch {
62
- return new Response('Invalid credentials', { status: 403 });
63
- }
64
- return new Response('Logged in', { status: 200 });
65
- }
66
- ```
67
35
 
68
- ### Identify Current User
36
+ The returned object exposes `username`, `role`, and `role.permission` flags.
69
37
 
70
- ```typescript
71
- async get() {
72
- const user = this.getCurrentUser?.();
73
- if (!user) return new Response(null, { status: 401 });
74
- return { username: user.username, role: user.role };
75
- }
76
- ```
38
+ 2. **Enable sessions** before using session-based login. Set `authentication.enableSessions: true` in `harperdb-config.yaml`:
77
39
 
78
- ### Sign Out Implementation
40
+ ```yaml
41
+ authentication:
42
+ enableSessions: true
43
+ ```
79
44
 
80
- ```typescript
81
- async post() {
82
- const context = this.getContext();
83
- await context.session?.delete?.(context.session.id);
84
- return new Response('Logged out', { status: 200 });
85
- }
86
- ```
45
+ 3. **Access login and session helpers** via `getContext()`. The context object exposes `context.login` and `context.session` for sign-in/out flows.
46
+ - Call `context.login(username, password)` to verify credentials and establish a session cookie on success.
47
+ - To end a session, delete it via `context.session.delete(context.session.id)`.
48
+
49
+ 4. **Implement sign-in and sign-out Resources** using the context helpers:
50
+
51
+ ```javascript
52
+ export class SignIn extends Resource {
53
+ async post(_target, data) {
54
+ const context = this.getContext();
55
+ try {
56
+ await context.login(data.username, data.password);
57
+ } catch {
58
+ return new Response('Invalid credentials', { status: 403 });
59
+ }
60
+ return new Response('Logged in', { status: 200 });
61
+ }
62
+ }
87
63
 
88
- ## Status code conventions used here
64
+ export class SignOut extends Resource {
65
+ async post() {
66
+ const context = this.getContext();
67
+ if (!context.session) return new Response(null, { status: 401 });
68
+ await context.session.delete(context.session.id);
69
+ return new Response('Logged out', { status: 200 });
70
+ }
71
+ }
72
+ ```
89
73
 
90
- - 200: Successful operation. For `GET /me`, a `200` with empty body means “not signed in”.
91
- - 400: Missing required fields (e.g., username/password on sign-in).
92
- - 401: No current session for an action that requires one (e.g., sign out when not signed in).
93
- - 403: Authenticated but not authorized (bad credentials on login attempt, or insufficient privileges).
74
+ 5. **Issue JWTs for non-browser clients** (CLI tools, mobile apps, service-to-service). Cookie-based sessions are intended for browser clients. For other clients, mint tokens programmatically using `server.operation()`:
75
+
76
+ ```javascript
77
+ import { Resource, server } from 'harper';
78
+
79
+ export class IssueTokens extends Resource {
80
+ static async get(_target, context) {
81
+ const { operation_token, refresh_token } = await server.operation(
82
+ { operation: 'create_authentication_tokens' },
83
+ context,
84
+ true,
85
+ );
86
+ return { operation_token, refresh_token };
87
+ }
88
+
89
+ static async post(_target, data) {
90
+ const { username, password } = await data;
91
+ if (!username || !password) {
92
+ return new Response('username and password required', { status: 400 });
93
+ }
94
+ const { operation_token, refresh_token } = await server.operation({
95
+ operation: 'create_authentication_tokens',
96
+ username,
97
+ password,
98
+ });
99
+ return { operation_token, refresh_token };
100
+ }
101
+ }
94
102
 
95
- ## Client considerations
103
+ export class RefreshJWT extends Resource {
104
+ static async post(_target, data) {
105
+ const { refresh_token } = await data;
106
+ if (!refresh_token) {
107
+ return new Response('refresh_token required', { status: 400 });
108
+ }
109
+ const { operation_token } = await server.operation({
110
+ operation: 'refresh_operation_token',
111
+ refresh_token,
112
+ });
113
+ return { operation_token };
114
+ }
115
+ }
116
+ ```
96
117
 
97
- - Sessions are cookie-based; the server handles setting and reading the cookie via Harper. If you make cross-origin requests, ensure the appropriate `credentials` mode and CORS settings.
98
- - If developing locally, double-check the server config still has `authentication.authorizeLocal: false` to avoid accidental superuser bypass.
118
+ Pass `true` as the third argument to `server.operation()` when the operation should run as the current authenticated user. Omit it or pass `false` when the operation supplies its own credentials.
99
119
 
100
- ## Token-based auth (JWT + refresh token) for non-browser clients
120
+ 6. **Configure JWT token expiry** in `harperdb-config.yaml` under the `authentication` section:
101
121
 
102
- Cookie-backed sessions are great for browser flows. For CLI tools, mobile apps, or other non-browser clients, it’s often easier to use **explicit tokens**:
122
+ ```yaml
123
+ authentication:
124
+ operationTokenTimeout: 1d
125
+ refreshTokenTimeout: 30d
126
+ ```
103
127
 
104
- - **JWT (`operation_token`)**: short-lived bearer token used to authorize API requests.
105
- - **Refresh token (`refresh_token`)**: longer-lived token used to mint a new JWT when it expires.
128
+ Duration strings follow the `jsonwebtoken` package format (e.g., `1d`, `12h`, `60m`).
106
129
 
107
- This project includes two Resource patterns for that flow:
130
+ ## Examples
108
131
 
109
- ### Issuing tokens: `IssueTokens`
132
+ **Protecting a resource endpoint and returning user info:**
110
133
 
111
- **Description / use case:** Generate `{ refreshToken, jwt }` either:
134
+ ```javascript
135
+ async get(target) {
136
+ const user = this.getCurrentUser();
137
+ if (!user) return new Response(null, { status: 401 });
138
+ return { username: user.username, role: user.role };
139
+ }
140
+ ```
112
141
 
113
- - with an existing Authorization token (either Basic Auth or a JWT) and you want to issue new tokens, or
114
- - from an explicit `{ username, password }` payload (useful for direct “login” from a CLI/mobile client).
142
+ **Full session-based sign-in/sign-out flow:**
115
143
 
116
144
  ```javascript
117
- export class IssueTokens extends Resource {
118
- static loadAsInstance = false;
119
-
120
- async get(target) {
121
- const { refresh_token: refreshToken, operation_token: jwt } =
122
- await databases.system.hdb_user.operation(
123
- { operation: 'create_authentication_tokens' },
124
- this.getContext(),
125
- );
126
- return { refreshToken, jwt };
127
- }
128
-
129
- async post(target, data) {
130
- if (!data.username || !data.password) {
131
- throw new Error('username and password are required');
145
+ export class SignIn extends Resource {
146
+ async post(_target, data) {
147
+ const context = this.getContext();
148
+ try {
149
+ await context.login(data.username, data.password);
150
+ } catch {
151
+ return new Response('Invalid credentials', { status: 403 });
132
152
  }
153
+ return new Response('Logged in', { status: 200 });
154
+ }
155
+ }
133
156
 
134
- const { refresh_token: refreshToken, operation_token: jwt } =
135
- await databases.system.hdb_user.operation({
136
- operation: 'create_authentication_tokens',
137
- username: data.username,
138
- password: data.password,
139
- });
140
- return { refreshToken, jwt };
157
+ export class SignOut extends Resource {
158
+ async post() {
159
+ const context = this.getContext();
160
+ if (!context.session) return new Response(null, { status: 401 });
161
+ await context.session.delete(context.session.id);
162
+ return new Response('Logged out', { status: 200 });
141
163
  }
142
164
  }
143
165
  ```
144
166
 
145
- **Recommended documentation notes to include:**
146
-
147
- - `GET` variant: intended for “I already have an Authorization token, give me new tokens”.
148
- - `POST` variant: intended for “I have credentials, give me tokens”.
149
- - Response shape:
150
- - `refreshToken`: store securely (long-lived).
151
- - `jwt`: attach to requests (short-lived).
152
-
153
- ### Refreshing a JWT: `RefreshJWT`
154
-
155
- **Description / use case:** When the JWT expires, the client uses the refresh token to get a new JWT without re-supplying username/password.
167
+ **JWT token refresh endpoint:**
156
168
 
157
169
  ```javascript
158
170
  export class RefreshJWT extends Resource {
159
- static loadAsInstance = false;
160
-
161
- async post(target, data) {
162
- if (!data.refreshToken) {
163
- throw new Error('refreshToken is required');
171
+ static async post(_target, data) {
172
+ const { refresh_token } = await data;
173
+ if (!refresh_token) {
174
+ return new Response('refresh_token required', { status: 400 });
164
175
  }
165
-
166
- const { operation_token: jwt } = await databases.system.hdb_user.operation({
176
+ const { operation_token } = await server.operation({
167
177
  operation: 'refresh_operation_token',
168
- refresh_token: data.refreshToken,
178
+ refresh_token,
169
179
  });
170
- return { jwt };
180
+ return { operation_token };
171
181
  }
172
182
  }
173
183
  ```
174
184
 
175
- **Recommended documentation notes to include:**
176
-
177
- - Requires `refreshToken` in the request body.
178
- - Returns a new `{ jwt }`.
179
- - If refresh fails (expired/revoked), client must re-authenticate (e.g., call `IssueTokens.post` again).
180
-
181
- ### Suggested client flow (high-level)
182
-
183
- 1. **Sign in (token flow)**
184
- - POST /IssueTokens/ with a body of `{ "username": "your username", "password": "your password" }` or GET /IssueTokens/ with an existing Authorization token.
185
- - Receive `{ jwt, refreshToken }` in the response
186
- 2. **Call protected APIs**
187
- - Send the JWT with each request in the Authorization header (as your auth mechanism expects)
188
- 3. **JWT expires**
189
- - POST /RefreshJWT/ with a body of `{ "refreshToken": "your refresh token" }`.
190
- - Receive `{ jwt }` in the response and continue
191
-
192
- ## Quick checklist
185
+ ## Notes
193
186
 
194
- - [ ] Public endpoints explicitly `allowRead`/`allowCreate` as needed.
195
- - [ ] Sign-in uses `context.login` and handles 400/403 correctly.
196
- - [ ] Protected routes call `ensureSuperUser(this.getCurrentUser())` (or another role check) before doing work.
197
- - [ ] Sign-out verifies a session and deletes it.
198
- - [ ] `authentication.authorizeLocal` is `false` and `enableSessions` is `true` in Harper config.
199
- - [ ] If using tokens: `IssueTokens` issues `{ jwt, refreshToken }`, `RefreshJWT` refreshes `{ jwt }` with a `refreshToken`.
187
+ - `getCurrentUser()` and `getContext()` are instance methods; call them with `this` inside non-static Resource methods.
188
+ - `enableSessions` must be `true` in config before `context.login` or `context.session` will function.
189
+ - Cookie-based sessions target browser clients. Use JWT issuance via `server.operation()` for all other client types.
190
+ - When both `operation_token` and `refresh_token` have expired, the client must call `create_authentication_tokens` again with credentials.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: creating-a-fabric-account-and-cluster
3
3
  description: How to create a Harper Fabric account, organization, and cluster.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Creating a Harper Fabric Account and Cluster
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: creating-harper-apps
3
3
  description: How to initialize a new Harper application using the CLI.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Creating Harper Applications
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: custom-resources
3
3
  description: How to define custom REST endpoints with JavaScript or TypeScript in Harper.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Custom Resources
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: defining-relationships
3
3
  description: How to define and use relationships between tables in Harper using GraphQL.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Defining Relationships
@@ -1,102 +1,122 @@
1
1
  ---
2
2
  name: deploying-to-harper-fabric
3
3
  description: How to deploy a Harper application to the Harper Fabric cloud.
4
+ metadata:
5
+ mode: generate
6
+ sources:
7
+ - reference/v5/components/applications.md#Remote Management
8
+ - >-
9
+ fabric/cluster-creation-management.md#Connecting the Harper CLI to a
10
+ Cluster
11
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
12
+ inputHash: ccdefbbf8f3b8657
4
13
  ---
5
14
 
6
15
  # Deploying to Harper Fabric
7
16
 
8
- Instructions for the agent to follow when deploying to Harper Fabric.
17
+ Instructions for the agent to follow when deploying a Harper application to the Harper Fabric cloud using the Harper CLI.
9
18
 
10
19
  ## When to Use
11
20
 
12
- Use this skill when you are ready to move your Harper application from local development to a cloud-hosted environment.
21
+ Apply this rule when deploying a Harper application to a remote Harper instance or Harper Fabric cluster. This covers interactive deployments, CI/CD pipelines, and any scenario where the agent must push a local or remote package to a target environment.
13
22
 
14
23
  ## How It Works
15
24
 
16
- 1. **Sign up**: Follow the [creating-a-fabric-account-and-cluster](creating-a-fabric-account-and-cluster.md) rule to create a Harper Fabric account, organization, and cluster.
17
- 2. **Configure Environment**: Add your cluster credentials and cluster application URL to `.env`:
25
+ 1. **Authenticate with the remote target**: Run `harper login` once to store an authentication token. The CLI writes `HARPER_CLI_TARGET` to a local `.env` so subsequent commands do not need credentials repeated. Find the **Application URL** on the cluster's **Config → Overview** page (see [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md)).
26
+
27
+ ```bash
28
+ harper login <Application URL>
29
+ # Provide cluster username and password when prompted
30
+ ```
31
+
32
+ 2. **Deploy the application**: Run `harper deploy` with the required parameters. After logging in, no credentials are needed inline.
33
+
34
+ ```bash
35
+ harper deploy \
36
+ project=<name> \
37
+ package=<package> \
38
+ target=<remote> \
39
+ restart=true \
40
+ replicated=true
41
+ ```
42
+
43
+ 3. **Choose a package source**: Set the `package` parameter to any valid npm dependency value, or omit it to package and deploy the current local directory.
44
+
45
+ | Value | Effect |
46
+ | ---------------------------------------------------- | ------------------------------------------------ |
47
+ | _(omitted)_ | Packages and deploys the current local directory |
48
+ | `"@harperdb/status-check"` | npm package |
49
+ | `"HarperDB/status-check"` | GitHub repo (short form) |
50
+ | `"https://github.com/HarperDB/status-check"` | GitHub repo (full URL) |
51
+ | `"git+ssh://git@github.com:HarperDB/secret-app.git"` | Private repo via SSH |
52
+ | `"https://example.com/application.tar.gz"` | Remote tarball |
53
+
54
+ For git tags, use the `semver` directive for reliable versioning:
55
+
56
+ ```
57
+ HarperDB/application-template#semver:v1.0.0
58
+ ```
59
+
60
+ 4. **Authenticate for CI/CD pipelines**: Use environment variables instead of interactive login. Set credentials before running `harper deploy`.
61
+
18
62
  ```bash
19
- CLI_TARGET_USERNAME='YOUR_CLUSTER_USERNAME'
20
- CLI_TARGET_PASSWORD='YOUR_CLUSTER_PASSWORD'
21
- CLI_TARGET='YOUR_CLUSTER_URL'
63
+ export HARPER_CLI_USERNAME=<username>
64
+ export HARPER_CLI_PASSWORD=<password>
65
+ harper deploy \
66
+ project=<name> \
67
+ package=<package> \
68
+ target=<remote> \
69
+ restart=true \
70
+ replicated=true
22
71
  ```
23
- 3. **Deploy From Local Environment**: Run `npm run deploy`.
24
- 4. **Set up CI/CD**: Configure `.github/workflows/deploy.yaml` and set repository secrets for automated deployments.
25
72
 
26
- ## Manual Setup for Existing Apps
73
+ 5. **Register SSH keys for private repos**: Before deploying from an SSH-based private repository, use the Add SSH Key operation to register the key with the remote instance.
74
+
75
+ ## Examples
27
76
 
28
- If your application was not created with `npm create harper`, you'll need to manually configure the deployment scripts and CI/CD workflow.
77
+ **Interactive login then deploy (recommended):**
29
78
 
30
- ### 1. Update `package.json`
79
+ ```bash
80
+ # Log in once
81
+ harper login <remote>
82
+ # Provide your username and password when prompted
31
83
 
32
- Add the following scripts and dependencies to your `package.json`:
84
+ # Subsequently deploy without credentials
85
+ harper deploy \
86
+ project=<name> \
87
+ package=<package> \
88
+ target=<remote> \
89
+ restart=true \
90
+ replicated=true
91
+ ```
33
92
 
34
- ```json
35
- {
36
- "scripts": {
37
- "deploy": "dotenv -- npm run deploy:component",
38
- "deploy:component": "harper deploy_component . restart=rolling replicated=true"
39
- },
40
- "devDependencies": {
41
- "dotenv-cli": "^11.0.0",
42
- "harper": "^5.0.0"
43
- }
44
- }
93
+ **Deploy with inline credentials (not recommended for production):**
94
+
95
+ ```bash
96
+ harper deploy \
97
+ project=<name> \
98
+ package=<package> \
99
+ username=<username> \
100
+ password=<password> \
101
+ target=<remote> \
102
+ restart=true \
103
+ replicated=true
45
104
  ```
46
105
 
47
- #### Why split the scripts?
48
-
49
- The `deploy` script is separated from `deploy:component` to ensure environment variables from your `.env` file are properly loaded and passed to the Harper CLI.
50
-
51
- - `deploy`: Uses `dotenv-cli` to load environment variables (like `CLI_TARGET`, `CLI_TARGET_USERNAME`, and `CLI_TARGET_PASSWORD`) before executing the next command.
52
- - `deploy:component`: The actual command that performs the deployment.
53
-
54
- By using `dotenv -- npm run deploy:component`, the environment variables are correctly set in the shell session before `harper deploy_component` is called, allowing it to authenticate with your cluster.
55
-
56
- ### 2. Configure GitHub Actions
57
-
58
- Create a `.github/workflows/deploy.yaml` file with the following content:
59
-
60
- ```yaml
61
- name: Deploy to Harper Fabric
62
- on:
63
- workflow_dispatch:
64
- # push:
65
- # branches:
66
- # - main
67
- concurrency:
68
- group: main
69
- cancel-in-progress: false
70
- jobs:
71
- deploy:
72
- runs-on: ubuntu-latest
73
- steps:
74
- - name: Checkout code
75
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
76
- with:
77
- fetch-depth: 0
78
- fetch-tags: true
79
- - name: Set up Node.js
80
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
81
- with:
82
- cache: 'npm'
83
- node-version-file: '.nvmrc'
84
- - name: Install dependencies
85
- run: npm ci
86
- - name: Run unit tests
87
- run: npm test
88
- - name: Run lint
89
- run: npm run lint
90
- - name: Deploy
91
- run: npm run deploy
92
- env:
93
- CLI_TARGET: ${{ secrets.CLI_TARGET }}
94
- CLI_TARGET_USERNAME: ${{ secrets.CLI_TARGET_USERNAME }}
95
- CLI_TARGET_PASSWORD: ${{ secrets.CLI_TARGET_PASSWORD }}
106
+ **Deploy a specific GitHub release by semver tag:**
107
+
108
+ ```bash
109
+ harper deploy \
110
+ project=my-app \
111
+ package="HarperDB/application-template#semver:v1.0.0" \
112
+ target=<remote> \
113
+ restart=true \
114
+ replicated=true
96
115
  ```
97
116
 
98
- Be sure to set the following repository secrets in your GitHub repository's /settings/secrets/actions:
117
+ ## Notes
99
118
 
100
- - `CLI_TARGET`
101
- - `CLI_TARGET_USERNAME`
102
- - `CLI_TARGET_PASSWORD`
119
+ - Always prefer `harper login` for interactive use and environment variables (`HARPER_CLI_USERNAME`, `HARPER_CLI_PASSWORD`) for CI/CD. Avoid inline `username`/`password` parameters in production.
120
+ - Omitting `package` causes the CLI to package the current local directory. Specifying a local file path creates a symlink, so changes are picked up between restarts without redeploying.
121
+ - Harper generates a `package.json` from component configurations and resolves dependencies using a form of `npm install`.
122
+ - For SSH-based private repos, register keys with the Add SSH Key operation before deploying.
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: extending-tables
3
3
  description: How to add custom logic to automatically generated table resources in Harper.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Extending Tables
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: handling-binary-data
3
3
  description: How to store and serve binary data like images or audio in Harper.
4
+ metadata:
5
+ mode: synthesized
4
6
  ---
5
7
 
6
8
  # Handling Binary Data