@harperfast/template-vue-ts-studio 1.9.0 → 1.9.2

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 (29) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +16 -12
  2. package/.agents/skills/harper-best-practices/rules/custom-resources.md +5 -4
  3. package/.agents/skills/harper-best-practices/rules/extending-tables.md +11 -8
  4. package/agent/skills/harper-best-practices/AGENTS.md +1950 -0
  5. package/agent/skills/harper-best-practices/SKILL.md +96 -0
  6. package/agent/skills/harper-best-practices/rules/adding-tables-with-schemas.md +42 -0
  7. package/agent/skills/harper-best-practices/rules/automatic-apis.md +165 -0
  8. package/agent/skills/harper-best-practices/rules/caching.md +163 -0
  9. package/agent/skills/harper-best-practices/rules/checking-authentication.md +190 -0
  10. package/agent/skills/harper-best-practices/rules/creating-a-fabric-account-and-cluster.md +30 -0
  11. package/agent/skills/harper-best-practices/rules/creating-harper-apps.md +54 -0
  12. package/agent/skills/harper-best-practices/rules/custom-resources.md +44 -0
  13. package/agent/skills/harper-best-practices/rules/defining-relationships.md +35 -0
  14. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +122 -0
  15. package/agent/skills/harper-best-practices/rules/extending-tables.md +46 -0
  16. package/agent/skills/harper-best-practices/rules/handling-binary-data.md +45 -0
  17. package/agent/skills/harper-best-practices/rules/load-env.md +111 -0
  18. package/agent/skills/harper-best-practices/rules/logging.md +169 -0
  19. package/agent/skills/harper-best-practices/rules/programmatic-table-requests.md +134 -0
  20. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +202 -0
  21. package/agent/skills/harper-best-practices/rules/real-time-apps.md +102 -0
  22. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +161 -0
  23. package/agent/skills/harper-best-practices/rules/serving-web-content.md +85 -0
  24. package/agent/skills/harper-best-practices/rules/typescript-type-stripping.md +64 -0
  25. package/agent/skills/harper-best-practices/rules/using-blob-datatype.md +38 -0
  26. package/agent/skills/harper-best-practices/rules/vector-indexing.md +124 -0
  27. package/agent/skills/harper-best-practices/rules.manifest.yaml +302 -0
  28. package/package.json +1 -1
  29. package/skills-lock.json +1 -1
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: creating-harper-apps
3
+ description: How to initialize a new Harper application using the CLI.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Creating Harper Applications
9
+
10
+ The fastest way to start a new Harper project is using the `create-harper` CLI tool. This command
11
+ initializes a project with a standard folder structure, essential configuration files, and basic
12
+ schema definitions.
13
+
14
+ ## When to Use
15
+
16
+ Use this command when starting a new Harper application or adding a new Harper microservice to an
17
+ existing architecture.
18
+
19
+ ## Commands
20
+
21
+ Initialize a project using your preferred package manager:
22
+
23
+ ### NPM
24
+
25
+ ```bash
26
+ npm create harper@latest
27
+ ```
28
+
29
+ ### PNPM
30
+
31
+ ```bash
32
+ pnpm create harper@latest
33
+ ```
34
+
35
+ ### Bun
36
+
37
+ ```bash
38
+ bun create harper@latest
39
+ ```
40
+
41
+ ## Options
42
+
43
+ You can specify the project name and template directly:
44
+
45
+ ```bash
46
+ npm create harper@latest my-app --template default
47
+ ```
48
+
49
+ ## Next Steps
50
+
51
+ 1. **Configure Environment**: Set up your `.env` file with local or cloud credentials.
52
+ 2. **Define Schema**: Modify `schema.graphql` to fit your application's data model.
53
+ 3. **Start Development**: Run `npm run dev` to start the local Harper instance.
54
+ 4. **Deploy**: Use `npm run deploy` to push your application to Harper Fabric.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: custom-resources
3
+ description: How to define custom REST endpoints with JavaScript or TypeScript in Harper.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Custom Resources
9
+
10
+ Instructions for the agent to follow when creating custom resources in Harper.
11
+
12
+ ## When to Use
13
+
14
+ Use this skill when the automatic CRUD operations provided by `@table @export` are insufficient, and you need custom logic, third-party API integration, or specialized data handling for your REST endpoints.
15
+
16
+ ## How It Works
17
+
18
+ 1. **Check if a Custom Resource is Necessary**: Verify if [Automatic APIs](./automatic-apis.md) or [Extending Tables](./extending-tables.md) can satisfy the requirement first.
19
+ 2. **Create the Resource File**: Create a `.ts` or `.js` file in the directory specified by `jsResource` in `config.yaml` (typically `resources/`).
20
+ 3. **Define the Resource Class**: Export a class extending `Resource` from `harper` and define **static** methods for the HTTP verbs you handle. In Harper 5 the static methods are the HTTP handlers (mapped 1:1 to verbs); they receive a pre-parsed `RequestTarget`, and write handlers also receive the request body as an awaitable `data` argument:
21
+
22
+ ```typescript
23
+ import { Resource } from 'harper';
24
+
25
+ export class MyResource extends Resource {
26
+ // v5 handlers are static and map 1:1 to HTTP verbs.
27
+ static async get(target: any) {
28
+ return { message: 'Hello from custom GET!' };
29
+ }
30
+ }
31
+ ```
32
+
33
+ 4. **Implement HTTP Methods**: Add static methods (`get`, `post`, `put`, `patch`, or `delete`) to handle the corresponding requests. Read/delete handlers receive `(target)`; write handlers receive `(target, data)` where `data` is awaitable.
34
+ 5. **Route Nesting and Naming**: You can control the URL structure by how you export your resources:
35
+ - **Direct Class Export**: `export class Foo extends Resource` creates endpoints at `/Foo/`. Class names are case-sensitive in the URL.
36
+ - **Nested Objects**: `export const Bar = { Foo };` creates endpoints at `/Bar/Foo/`.
37
+ - **Lowercase and Hyphens**: Use object keys to define custom paths: `export const bar = { 'foo-baz': Foo };` exposes endpoints at `/bar/foo-baz/`.
38
+ 6. **Access Tables (Optional)**: Import and use the `tables` object to interact with your data:
39
+ ```typescript
40
+ import { tables } from 'harper';
41
+ // ... inside a method
42
+ const results = await tables.MyTable.list();
43
+ ```
44
+ 7. **Configure Loading**: Ensure `config.yaml` points to your resource files (e.g., `jsResource: { files: 'resources/*.ts' }`).
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: defining-relationships
3
+ description: How to define and use relationships between tables in Harper using GraphQL.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Defining Relationships
9
+
10
+ Instructions for the agent to follow when defining relationships between Harper tables.
11
+
12
+ ## When to Use
13
+
14
+ Use this skill when you need to link data across different tables, enabling automatic joins and efficient related-data fetching via REST APIs.
15
+
16
+ ## How It Works
17
+
18
+ 1. **Identify the Relationship Type**: Determine if it's one-to-one, many-to-one, or one-to-many.
19
+ 2. **Use the `@relationship` Directive**: Apply it to a field in your GraphQL schema.
20
+ - **Many-to-One (Current table holds FK)**: Use `from`.
21
+ ```graphql
22
+ type Book @table @export {
23
+ authorId: ID
24
+ author: Author @relationship(from: "authorId")
25
+ }
26
+ ```
27
+ - **One-to-Many (Related table holds FK)**: Use `to` and an array type.
28
+ ```graphql
29
+ type Author @table @export {
30
+ books: [Book] @relationship(to: "authorId")
31
+ }
32
+ ```
33
+ 3. **Query with Relationships**: Use dot syntax in REST API calls for filtering or the `select()` operator for including related data.
34
+ - Example Filter: `GET /Book/?author.name=Harper`
35
+ - Example Select: `GET /Author/?select(name,books(title))`
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: deploying-to-harper-fabric
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
13
+ ---
14
+
15
+ # Deploying to Harper Fabric
16
+
17
+ Instructions for the agent to follow when deploying a Harper application to the Harper Fabric cloud using the Harper CLI.
18
+
19
+ ## When to Use
20
+
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.
22
+
23
+ ## How It Works
24
+
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
+
62
+ ```bash
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
71
+ ```
72
+
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
76
+
77
+ **Interactive login then deploy (recommended):**
78
+
79
+ ```bash
80
+ # Log in once
81
+ harper login <remote>
82
+ # Provide your username and password when prompted
83
+
84
+ # Subsequently deploy without credentials
85
+ harper deploy \
86
+ project=<name> \
87
+ package=<package> \
88
+ target=<remote> \
89
+ restart=true \
90
+ replicated=true
91
+ ```
92
+
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
104
+ ```
105
+
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
115
+ ```
116
+
117
+ ## Notes
118
+
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.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: extending-tables
3
+ description: How to add custom logic to automatically generated table resources in Harper.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Extending Tables
9
+
10
+ Instructions for the agent to follow when extending table resources in Harper.
11
+
12
+ ## When to Use
13
+
14
+ Use this skill when you need to add custom validation, side effects (like webhooks), data transformation, or custom access control to the standard CRUD operations of a Harper table.
15
+
16
+ ## How It Works
17
+
18
+ 1. **Define the Table in GraphQL**: In your `.graphql` schema, define the table using the `@table` directive. **Do not** use `@export` if you plan to extend it.
19
+ ```graphql
20
+ type MyTable @table {
21
+ id: ID @primaryKey
22
+ name: String
23
+ }
24
+ ```
25
+ 2. **Create the Extension File**: Create a `.ts` file in your `resources/` directory.
26
+ 3. **Extend the Table Resource**: Export a class that extends `tables.YourTableName` and override the relevant **static** methods. In Harper 5 resource handlers are static and map 1:1 to HTTP verbs: `get(target)`, `post(target, data)`, `put(target, data)`, `patch(target, data)`, `delete(target)`. `target` is a pre-parsed `RequestTarget`; for writes, `data` is the request body and is **awaitable** (`await data`). Delegate to `super` to keep Harper's default behavior — a collection create passes just the record (`super.post(record)`), updates pass the target (`super.put(target, data)` / `super.patch(target, data)`), and reads/deletes pass the target (`super.get(target)`). To return a specific HTTP status from a thrown error, set **`.statusCode`** (e.g. `400`) on the error — a plain `.status` property is ignored.
27
+
28
+ ```typescript
29
+ import { tables } from 'harper';
30
+
31
+ export class MyTable extends tables.MyTable {
32
+ // Static handler; receives (target, data) — data is awaitable.
33
+ static async post(target: any, data: any) {
34
+ const record = await data;
35
+ if (!record?.name) {
36
+ const error: any = new Error('Name is required');
37
+ error.statusCode = 400; // HTTP status (use statusCode, NOT status)
38
+ throw error;
39
+ }
40
+ return super.post(record); // create delegates with the record (no id)
41
+ }
42
+ }
43
+ ```
44
+
45
+ 4. **Override Methods**: Override the static `get`, `post`, `put`, `patch`, or `delete` as needed, delegating to `super.<method>` (see the argument forms above) to preserve Harper's default behavior unless you intend to replace it entirely.
46
+ 5. **Implement Logic**: Use overrides for validation, side effects, or transforming data before/after database operations.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: handling-binary-data
3
+ description: How to store and serve binary data like images or audio in Harper.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Handling Binary Data
9
+
10
+ Instructions for the agent to follow when handling binary data in Harper.
11
+
12
+ ## When to Use
13
+
14
+ Use this skill when you need to store binary files (images, audio, etc.) in the database or serve them back to clients via REST endpoints.
15
+
16
+ ## How It Works
17
+
18
+ 1. **Store Binary Data**: In your resource's `post` or `put` method, convert incoming data to Buffers and then to Blobs using `createBlob` from Harper's globals. Include the MIME type if available:
19
+
20
+ ```typescript
21
+ async post(target, record) {
22
+ if (record.data) {
23
+ record.data = createBlob(Buffer.from(record.data, record.encoding || 'base64'), {
24
+ type: record.contentType || 'application/octet-stream',
25
+ });
26
+ }
27
+ return super.post(target, record);
28
+ }
29
+ ```
30
+
31
+ 2. **Serve Binary Data**: In your resource's `get` method, return a response object with the appropriate `Content-Type` and the binary data in the `body`:
32
+ ```typescript
33
+ async get(target) {
34
+ const record = await super.get(target);
35
+ if (record?.data) {
36
+ return {
37
+ status: 200,
38
+ headers: { 'Content-Type': record.data.type || 'application/octet-stream' },
39
+ body: record.data,
40
+ };
41
+ }
42
+ return record;
43
+ }
44
+ ```
45
+ 3. **Use the Blob Type**: Ensure your GraphQL schema uses the `Blob` scalar for binary fields.
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: load-env
3
+ description: >-
4
+ How to load environment variables from .env files into a Harper application
5
+ using the loadEnv plugin.
6
+ metadata:
7
+ mode: generate
8
+ sources:
9
+ - reference/v5/environment-variables/overview.md
10
+ sourceCommit: 42231db17c025a4a455e3d91875fad4084a01cb5
11
+ inputHash: fe610fc245226357
12
+ ---
13
+
14
+ # Load Environment Variables with loadEnv
15
+
16
+ Instructions for the agent to follow when loading environment variables from `.env` files into a Harper application using the `loadEnv` plugin.
17
+
18
+ ## When to Use
19
+
20
+ Apply this rule when a Harper application needs to load secrets or configuration values from `.env` files into `process.env` at startup. Use it whenever hardcoding values must be avoided and environment-specific configuration must be supplied to Harper components.
21
+
22
+ ## How It Works
23
+
24
+ 1. **Declare `loadEnv` in `config.yaml`**: Add `loadEnv` as a top-level key. It is built into Harper and requires no installation.
25
+
26
+ ```yaml
27
+ loadEnv:
28
+ files: '.env'
29
+ ```
30
+
31
+ 2. **Place `loadEnv` first**: Always list `loadEnv` before any other components in `config.yaml` so that environment variables are available on `process.env` before dependent components start.
32
+
33
+ ```yaml
34
+ # config.yaml — loadEnv must come first
35
+ loadEnv:
36
+ files: '.env'
37
+
38
+ rest: true
39
+
40
+ myApp:
41
+ files: './src/*.js'
42
+ ```
43
+
44
+ 3. **Configure the `files` option**: Provide one or more paths or glob patterns pointing to the env files to load. This option is required.
45
+
46
+ 4. **Set `override` if needed**: By default, existing environment variables take precedence over values in `.env` files. Set `override: true` to reverse this and have loaded values win.
47
+
48
+ ```yaml
49
+ loadEnv:
50
+ files: '.env'
51
+ override: true
52
+ ```
53
+
54
+ 5. **Load multiple files when required**: Supply a list of files or a glob pattern. Files are loaded in the order specified.
55
+ ```yaml
56
+ loadEnv:
57
+ files:
58
+ - '.env'
59
+ - '.env.local'
60
+ ```
61
+ or
62
+ ```yaml
63
+ loadEnv:
64
+ files: 'env-vars/*'
65
+ ```
66
+
67
+ ### Configuration Options
68
+
69
+ | Option | Type | Required | Description |
70
+ | ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------- |
71
+ | `files` | `string \| string[]` | **Yes** | Path(s) or glob pattern(s) to the env file(s) to load. |
72
+ | `override` | `boolean` | No | If `true`, loaded values override existing environment variables. Defaults to `false`. |
73
+
74
+ ## Examples
75
+
76
+ **Minimal setup — single `.env` file:**
77
+
78
+ ```yaml
79
+ loadEnv:
80
+ files: '.env'
81
+ ```
82
+
83
+ **Full `config.yaml` with load order, multiple files, and override:**
84
+
85
+ ```yaml
86
+ # config.yaml — loadEnv must come first
87
+ loadEnv:
88
+ files:
89
+ - '.env'
90
+ - '.env.local'
91
+ override: true
92
+
93
+ rest: true
94
+
95
+ myApp:
96
+ files: './src/*.js'
97
+ ```
98
+
99
+ **Glob pattern:**
100
+
101
+ ```yaml
102
+ loadEnv:
103
+ files: 'env-vars/*'
104
+ ```
105
+
106
+ ## Notes
107
+
108
+ - `loadEnv` is built into Harper — do not install it separately; only declare it in `config.yaml`.
109
+ - Because Harper is a single-process application, variables loaded onto `process.env` are shared across all components.
110
+ - Without `override: true`, variables already set in the shell or container environment will not be overwritten by values in `.env` files.
111
+ - `files` is the only required option; omitting it will produce an invalid configuration.
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: logging
3
+ description: >-
4
+ Best practices for logging in Harper, including console capture, the granular
5
+ logger interface, and programmatic log retrieval.
6
+ metadata:
7
+ mode: generate
8
+ sources:
9
+ - reference/v5/logging/overview.md
10
+ - reference/v5/logging/api.md
11
+ sourceCommit: b7fbddadd42eb4487190b650a9abc4bcfeef5819
12
+ inputHash: 46cd384598304e3b
13
+ ---
14
+
15
+ # Harper Logging
16
+
17
+ Instructions for the agent to follow when implementing logging in Harper applications, including direct logger usage, tagged loggers, and console capture behavior.
18
+
19
+ ## When to Use
20
+
21
+ Apply this rule when writing any JavaScript component, plugin, or resource that needs to emit structured log entries, filter logs by component, or capture existing `console.log` output into Harper's log system. Use it whenever you need to understand log levels, log entry format, or the `logger` global API.
22
+
23
+ ## How It Works
24
+
25
+ 1. **Use the `logger` global directly** — `logger` is available in all JavaScript components without any imports. Call the method matching the desired severity level:
26
+
27
+ ```javascript
28
+ logger.trace('detailed trace message');
29
+ logger.debug('debug info', { someContext: 'value' });
30
+ logger.info('informational message');
31
+ logger.warn('potential issue');
32
+ logger.error('error occurred', error);
33
+ logger.fatal('fatal error');
34
+ logger.notify('server is ready');
35
+ ```
36
+
37
+ Only entries at or above the configured `logging.level` (or `logging.external.level`) are written to `hdb.log`.
38
+
39
+ 2. **Create a tagged logger with `withTag(`** — Call `logger.withTag(tag)` once per module or class to get a `TaggedLogger` scoped to that tag. This prefixes every log entry with the tag, making log output filterable by component.
40
+
41
+ ```javascript
42
+ const log = logger.withTag('my-resource');
43
+ ```
44
+
45
+ Because `TaggedLogger` methods for disabled levels are `null`, always use optional chaining (`?.`) when calling them:
46
+
47
+ ```javascript
48
+ log.debug?.('Fetching record', { id });
49
+ log.warn?.('Record not found', { id });
50
+ log.error?.('Failed to update record', err);
51
+ ```
52
+
53
+ `TaggedLogger` does not have a `withTag()` method.
54
+
55
+ 3. **Understand the interface contracts** — `MainLogger` always has all methods defined:
56
+
57
+ ```typescript
58
+ interface MainLogger {
59
+ trace(...messages: any[]): void;
60
+ debug(...messages: any[]): void;
61
+ info(...messages: any[]): void;
62
+ warn(...messages: any[]): void;
63
+ error(...messages: any[]): void;
64
+ fatal(...messages: any[]): void;
65
+ notify(...messages: any[]): void;
66
+ withTag(tag: string): TaggedLogger;
67
+ }
68
+ ```
69
+
70
+ `TaggedLogger` methods may be `null`:
71
+
72
+ ```typescript
73
+ interface TaggedLogger {
74
+ trace: ((...messages: any[]) => void) | null;
75
+ debug: ((...messages: any[]) => void) | null;
76
+ info: ((...messages: any[]) => void) | null;
77
+ warn: ((...messages: any[]) => void) | null;
78
+ error: ((...messages: any[]) => void) | null;
79
+ fatal: ((...messages: any[]) => void) | null;
80
+ notify: ((...messages: any[]) => void) | null;
81
+ }
82
+ ```
83
+
84
+ 4. **Know the log levels** — From least to most severe:
85
+
86
+ | Level | Description |
87
+ | -------- | -------------------------------------------------------------------- |
88
+ | `trace` | Highly detailed internal execution tracing. |
89
+ | `debug` | Diagnostic information useful during development. |
90
+ | `info` | General operational events. |
91
+ | `warn` | Potential issues that don't prevent normal operation. |
92
+ | `error` | Errors that affect specific operations. |
93
+ | `fatal` | Critical errors causing process termination. |
94
+ | `notify` | Important operational milestones. Always logged regardless of level. |
95
+
96
+ The default log level is `warn`. Setting a level includes that level and all more-severe levels.
97
+
98
+ 5. **Enable console capture when porting existing code** — When `logging.console: true` is set, writes via `console.log`, `console.warn`, `console.error`, etc. are appended verbatim to `hdb.log`. Captured lines do **not** pass through `logger`'s level filter. Prefer `logger` directly in production code so that level filtering and tagging apply. Console capture is intended as a convenience for porting existing code and for debugging.
99
+
100
+ 6. **Know where logs are written** — All standard log output goes to `<ROOTPATH>/log/hdb.log` (default: `~/hdb/log/hdb.log`). To also log to `stdout`/`stderr`, set `logging.stdStreams: true`.
101
+
102
+ ## Examples
103
+
104
+ ### Basic logging in a resource
105
+
106
+ ```javascript
107
+ export class MyResource extends Resource {
108
+ async get(id) {
109
+ logger.debug('Fetching record', { id });
110
+ const record = await super.get(id);
111
+ if (!record) {
112
+ logger.warn('Record not found', { id });
113
+ }
114
+ return record;
115
+ }
116
+
117
+ async put(record) {
118
+ logger.info('Updating record', { id: record.id });
119
+ try {
120
+ return await super.put(record);
121
+ } catch (err) {
122
+ logger.error('Failed to update record', err);
123
+ throw err;
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Tagged logging with `withTag()`
130
+
131
+ ```javascript
132
+ const log = logger.withTag('my-resource');
133
+
134
+ export class MyResource extends Resource {
135
+ async get(id) {
136
+ log.debug?.('Fetching record', { id });
137
+ const record = await super.get(id);
138
+ if (!record) {
139
+ log.warn?.('Record not found', { id });
140
+ }
141
+ return record;
142
+ }
143
+
144
+ async put(record) {
145
+ log.info?.('Updating record', { id: record.id });
146
+ try {
147
+ return await super.put(record);
148
+ } catch (err) {
149
+ log.error?.('Failed to update record', err);
150
+ throw err;
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ Tagged entries appear in `hdb.log` with the tag in the header:
157
+
158
+ ```
159
+ 2023-03-09T14:25:05.269Z [info] [my-resource]: Updating record
160
+ ```
161
+
162
+ ## Notes
163
+
164
+ - All log output is written to `<ROOTPATH>/log/hdb.log`. The `logger` global writes to this file at the configured `logging.external` level.
165
+ - Log entry format for `logger`: `<timestamp> [<level>] [<thread>/<id>]: <message>`
166
+ - Log entry format for `TaggedLogger`: `<timestamp> [<level>] [<tag>]: <message>`
167
+ - `console.log` output is only forwarded to `hdb.log` when `logging.console: true` is explicitly set; it is not forwarded by default.
168
+ - When logging to standard streams, run Harper in the foreground (`harper`, not `harper start`).
169
+ - `TaggedLogger` is bound to the configured log level at creation time — always use `?.` on its methods.