@harperfast/template-vue-ts-studio 1.12.11 → 1.13.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.
@@ -1605,7 +1605,7 @@ Instructions for the agent to follow when defining custom REST endpoints with Ja
1605
1605
 
1606
1606
  #### When to Use
1607
1607
 
1608
- Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or registering routes programmatically in a Harper application. Use it any time business logic must live outside a table-backed resource or when a specific URL shape is required.
1608
+ Apply this rule when you need to create custom HTTP endpoints, wrap external APIs, or register routes programmatically in a Harper application. Use it any time business logic must live outside a table-backed schema, or when you need fine-grained control over URL shape and path parameters.
1609
1609
 
1610
1610
  #### How It Works
1611
1611
 
@@ -1615,7 +1615,13 @@ Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or
1615
1615
  import { tables, Resource } from 'harper';
1616
1616
  ```
1617
1617
 
1618
- 2. **Define a class that `extends Resource`**: Implement HTTP methods as `static` methods. Each method receives a `target` object.
1618
+ CommonJS alternative:
1619
+
1620
+ ```javascript
1621
+ const { tables, Resource } = require('harper');
1622
+ ```
1623
+
1624
+ 2. **Define a class that `extends Resource`**: Use `export class` so Harper exposes it as an endpoint. Implement HTTP methods as `static` methods on the class.
1619
1625
 
1620
1626
  ```javascript
1621
1627
  export class CustomEndpoint extends Resource {
@@ -1627,7 +1633,7 @@ Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or
1627
1633
  }
1628
1634
  ```
1629
1635
 
1630
- 3. **Use `async` static methods to call external services**: Return or forward the response directly.
1636
+ 3. **Use `static` async methods to call external services**: Each HTTP verb maps to a `static` method of the same lowercase name.
1631
1637
 
1632
1638
  ```javascript
1633
1639
  export class MyExternalData extends Resource {
@@ -1645,24 +1651,20 @@ Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or
1645
1651
  }
1646
1652
  ```
1647
1653
 
1648
- 4. **Export the class to expose it as an endpoint**: The export form controls the resulting URL. Choose the form that matches the URL shape you need.
1654
+ 4. **Control the URL by choosing the export form**: The shape of the export determines the resulting URL path.
1649
1655
 
1650
1656
  | Export form | URL | Notes |
1651
1657
  | ------------------------------------------- | --------------- | --------------------------------------------------------------- |
1652
- | `export class Foo extends Resource {}` | `/Foo/` | Class name becomes the path segment. Case-sensitive. |
1658
+ | `export class Foo extends Resource {}` | `/Foo/` | Class name becomes the path segment; case-sensitive. |
1653
1659
  | `export const Bar = { Foo };` | `/Bar/Foo/` | Nest under an object to add a path prefix. |
1654
1660
  | `export const bar = { 'foo-baz': Foo };` | `/bar/foo-baz/` | Use object keys for lowercase, hyphens, or non-identifier URLs. |
1655
1661
  | `export { Foo as '/widget/:id' }` | `/widget/:id` | Rename the export to set the path directly. |
1656
1662
  | `static path = '/widget/:id'` (class field) | `/widget/:id` | Declare path on the class; overrides the export name. |
1657
- | `server.resources.set('my-path', Foo);` | `/my-path/` | Programmatic registration for dynamic paths. |
1663
+ | `server.resources.set('my-path', Foo);` | `/my-path/` | Programmatic registration; useful when the path is dynamic. |
1658
1664
 
1659
- 5. **Register programmatically when the path is dynamic**: Use `server.resources.set(` with a path string and the resource class.
1665
+ URL path matching is case-sensitive — `/Foo/` and `/foo/` are different endpoints.
1660
1666
 
1661
- ```javascript
1662
- server.resources.set('my-path', Foo);
1663
- ```
1664
-
1665
- 6. **Declare dynamic path segments with `static path`**: Use `:name` for a single segment and `*name` as a catch-all. Matched values are bound onto `target.<name>`.
1667
+ 5. **Declare dynamic path segments with `static path`**: Use `:name` for a single segment and `*name` as a catch-all. Matched values are bound onto `target.<name>`.
1666
1668
 
1667
1669
  ```javascript
1668
1670
  export class Widget extends Resource {
@@ -1672,13 +1674,36 @@ Apply this rule when creating custom HTTP endpoints, wrapping external APIs, or
1672
1674
  return { id: target.id, action: target.action };
1673
1675
  }
1674
1676
  }
1677
+
1678
+ export class Files extends Resource {
1679
+ // GET /files/a/b/c.txt -> target.rest === 'a/b/c.txt'
1680
+ static path = '/files/*rest';
1681
+ static get(target) {
1682
+ return { path: target.rest };
1683
+ }
1684
+ }
1685
+ ```
1686
+
1687
+ - A leading `/` in `static path` makes the path root-relative (top-level), independent of the file's location.
1688
+ - A leading `./` or bare name resolves relative to the component directory.
1689
+ - A bare `*` (no name) binds under `target.wildcard`. A wildcard must be the final segment.
1690
+ - `static path` takes precedence over the export name.
1691
+ - Exact and static paths always win over parameterized ones. Among parameterized routes, more specific paths win: a literal segment beats `:param`, which beats `*`, compared left to right.
1692
+
1693
+ 6. **Register programmatically when the path is dynamic**: Use `server.resources.set(` with a path string and the resource class.
1694
+
1695
+ ```javascript
1696
+ server.resources.set('my-path', Foo);
1675
1697
  ```
1676
1698
 
1677
- 7. **Resolve path precedence correctly**: Exact and static paths always win over parameterized ones. Among parameterized routes, more specific paths win: a literal segment beats a `:param`, which beats a `*` wildcard, compared left to right.
1699
+ 7. **Optionally use a resource as a cache source for a local table**: Call `sourcedFrom` on the target table.
1700
+ ```javascript
1701
+ tables.MyCache.sourcedFrom(MyExternalData);
1702
+ ```
1678
1703
 
1679
1704
  #### Examples
1680
1705
 
1681
- **Wrapping an external API and using it as a cache source:**
1706
+ **Custom endpoint with external API wrapping:**
1682
1707
 
1683
1708
  ```javascript
1684
1709
  import { tables, Resource } from 'harper';
@@ -1701,9 +1726,19 @@ export class MyExternalData extends Resource {
1701
1726
  tables.MyCache.sourcedFrom(MyExternalData);
1702
1727
  ```
1703
1728
 
1704
- **Catch-all wildcard path:**
1729
+ **Path parameters with `static path`:**
1705
1730
 
1706
1731
  ```javascript
1732
+ import { Resource } from 'harper';
1733
+
1734
+ export class Widget extends Resource {
1735
+ // GET /widget/10/action/jump -> target.id === '10', target.action === 'jump'
1736
+ static path = '/widget/:id/action/:action';
1737
+ static get(target) {
1738
+ return { id: target.id, action: target.action };
1739
+ }
1740
+ }
1741
+
1707
1742
  export class Files extends Resource {
1708
1743
  // GET /files/a/b/c.txt -> target.rest === 'a/b/c.txt'
1709
1744
  static path = '/files/*rest';
@@ -1713,27 +1748,26 @@ export class Files extends Resource {
1713
1748
  }
1714
1749
  ```
1715
1750
 
1716
- **Root-relative fixed route:**
1751
+ **Programmatic registration:**
1717
1752
 
1718
1753
  ```javascript
1719
- export class AcmeChallenge extends Resource {
1720
- static path = '/.well-known/acme-challenge/:token';
1754
+ import { Resource } from 'harper';
1755
+
1756
+ export class Foo extends Resource {
1721
1757
  static get(target) {
1722
- return { token: target.token };
1758
+ return { ok: true };
1723
1759
  }
1724
1760
  }
1761
+
1762
+ server.resources.set('my-path', Foo);
1725
1763
  ```
1726
1764
 
1727
1765
  #### Notes
1728
1766
 
1729
- - URL path matching is case-sensitive — `/Foo/` and `/foo/` are different endpoints.
1730
- - A leading `/` in `static path` makes the path root-relative (top-level), independent of the file's location.
1731
- - A leading `./` or a bare name in `static path` resolves relative to the component directory.
1732
- - A bare `*` (no name) binds under `target.wildcard`. A wildcard must be the final segment of the path.
1733
- - `static path` takes precedence over the export name when both are present.
1734
- - Parameterized routes appear in the generated OpenAPI document as templated paths (e.g. `/widget/{id}/action/{action}`) and in MCP `resources/templates/list` as URI templates.
1735
- - When a resource `extends` an existing table, avoid conflicting exports between the schema and the JavaScript implementation.
1736
- - Link the `harper` package in your component directory to ensure correct typings: `npm link harper`. All installed components have `harper` automatically linked.
1767
+ - Avoid conflicting exports between the schema and the JavaScript implementation when a resource `extends` an existing table.
1768
+ - Parameterized routes appear in the generated OpenAPI document as templated paths (e.g. `/widget/{id}/action/{action}`) and in MCP `resources/templates/list` as `{param}` URI templates.
1769
+ - When bundling for SSR with a tool like Vite, keep `harper` external so it resolves to the runtime: `ssr: { external: ['harper'] }`.
1770
+ - For components in their own directory, run `npm link harper` to ensure typings match the running installation.
1737
1771
 
1738
1772
  ### 3.2 Extending Tables
1739
1773
 
@@ -1833,25 +1867,31 @@ export class MyTable extends tables.MyTable {
1833
1867
 
1834
1868
  ### 3.3 Programmatic Table Requests
1835
1869
 
1836
- Instructions for the agent to interact with Harper tables programmatically using the `tables` object, the Query API, and transactions.
1870
+ Instructions for the agent to follow when interacting with Harper tables programmatically using the `tables` object.
1837
1871
 
1838
1872
  #### When to Use
1839
1873
 
1840
- Apply this rule when writing server-side Harper component code that reads from or writes to tables directly — for example, in HTTP handlers, background jobs, timers, or SSR render functions. Use it whenever you need to construct queries with `conditions`, `sort`, `select`, `limit`, or `offset`, or when you need explicit transaction control via `transaction()`.
1874
+ Apply this rule when writing server-side Harper component code that reads from or writes to tables using the programmatic API — for example, in HTTP handlers, background jobs, timers, or SSR entry points. Use it whenever you need to construct queries with `conditions`, `sort`, `select`, pagination, or transactions outside of the REST layer.
1841
1875
 
1842
1876
  #### How It Works
1843
1877
 
1844
- 1. **Import `tables` (and other APIs) from `harper`**: Access every table defined in `schema.graphql` as a named property of `tables`. Each property is the table class implementing the Resource API.
1878
+ 1. **Import `tables` (and other APIs) from `harper`**: Access every table defined in `schema.graphql` as a property of `tables`. Each property is the table class implementing the Resource API.
1845
1879
 
1846
1880
  ```javascript
1847
1881
  import { tables, transaction } from 'harper';
1848
1882
  const { Product } = tables;
1849
- // equivalent to: databases.data.Product
1883
+ // same as: databases.data.Product
1850
1884
  ```
1851
1885
 
1852
- For standalone components, run `npm link harper` so imports resolve to the live runtime.
1886
+ For components in their own directory, run:
1853
1887
 
1854
- 2. **Define your schema with `@table`**: Tables must be declared in `schema.graphql`. Mark attributes you intend to sort or filter on with `@indexed`.
1888
+ ```bash
1889
+ npm link harper
1890
+ ```
1891
+
1892
+ All installed components have `harper` automatically linked.
1893
+
1894
+ 2. **Define your schema with `@table`**: Every type you want to access via `tables` must carry the `@table` directive. Mark queryable fields with `@indexed`.
1855
1895
 
1856
1896
  ```graphql
1857
1897
  type Product @table {
@@ -1861,7 +1901,7 @@ Apply this rule when writing server-side Harper component code that reads from o
1861
1901
  }
1862
1902
  ```
1863
1903
 
1864
- 3. **Query records with `search(`**: Pass a Query object to `search(`. Iterate results with `for await`.
1904
+ 3. **Query records using `search(`**: Pass a Query object to `search()`. The query is an async iterable.
1865
1905
 
1866
1906
  ```javascript
1867
1907
  const query = {
@@ -1872,7 +1912,7 @@ Apply this rule when writing server-side Harper component code that reads from o
1872
1912
  }
1873
1913
  ```
1874
1914
 
1875
- 4. **Build `conditions`**: Each condition object supports these properties:
1915
+ 4. **Build `conditions`**: Each condition object supports the following properties:
1876
1916
 
1877
1917
  | Property | Description |
1878
1918
  | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -1882,53 +1922,60 @@ Apply this rule when writing server-side Harper component code that reads from o
1882
1922
  | `conditions` | Nested conditions array |
1883
1923
  | `operator` | `and` (default) or `or` for the nested `conditions` |
1884
1924
 
1885
- 5. **Control result shape with `select`**: Pass an array of property names, a single string, or nested objects for relationships.
1925
+ 5. **Apply `select` to shape results**: Pass an array of property names, a string for a single property, or nested objects for relationships.
1886
1926
 
1887
1927
  ```javascript
1888
- // Scalar fields only
1928
+ // Flat select
1889
1929
  Product.search({ select: ['name', 'price'] });
1890
1930
 
1891
- // Partial related record
1931
+ // Nested relationship select
1892
1932
  Book.get({ id: 42, select: ['id', 'title', { name: 'author', select: ['name'] }] });
1893
1933
  ```
1894
1934
 
1895
1935
  Special `select` values: `$id`, `$updatedtime`, `$distance`.
1896
1936
 
1897
- 6. **Paginate with `limit` and `offset`**:
1937
+ 6. **Apply `sort`**: Harper uses an index to provide sort order.
1898
1938
 
1899
- ```javascript
1900
- Product.search({ conditions: [...], limit: 20, offset: 40 });
1901
- ```
1939
+ | Property | Description |
1940
+ | ------------ | ---------------------------------------------------------- |
1941
+ | `attribute` | Property name (or array for chained relationship property) |
1942
+ | `descending` | Sort descending if `true` (default: `false`) |
1943
+ | `next` | Secondary sort to resolve ties (same structure) |
1944
+ - If the sort `attribute` is `@indexed`, no condition is required.
1945
+ - If the sort `attribute` is not indexed, at least one entry in `conditions` (on **any** attribute) is required.
1946
+ - Sorting by a non-indexed attribute with zero conditions raises:
1947
+ > `HdbError: <attribute> is not indexed and not combined with any other conditions`
1948
+ - The bare `@primaryKey` is treated as not indexed for sort purposes. To iterate in primary-key order, add an open-ended condition:
1902
1949
 
1903
- 7. **Sort with `sort`**: The `sort` object accepts `attribute`, `descending` (default `false`), and `next` for tie-breaking. Harper uses an index for sort order — the sort `attribute` must be `@indexed`, **or** at least one entry in `conditions` must be present.
1950
+ ```javascript
1951
+ Product.search({
1952
+ conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],
1953
+ sort: { attribute: 'id' },
1954
+ });
1955
+ ```
1904
1956
 
1905
- Sorting by a non-indexed attribute with zero conditions raises:
1957
+ - Pass `allowFullScan: true` to permit an unconditional ordered scan, or omit `sort` entirely to iterate without an index requirement.
1906
1958
 
1907
- > `HdbError: <attribute> is not indexed and not combined with any other conditions`
1908
-
1909
- Note: `@primaryKey` alone is treated as not indexed for sort purposes. To scan the whole table in primary-key order, add an open-ended condition:
1959
+ 7. **Paginate with `limit` and `offset`**:
1910
1960
 
1911
1961
  ```javascript
1912
- Product.search({
1913
- conditions: [{ attribute: 'id', comparator: 'greater_than', value: '' }],
1914
- sort: { attribute: 'id' },
1915
- });
1962
+ Product.search({ conditions: [...], limit: 20, offset: 40 });
1916
1963
  ```
1917
1964
 
1918
- Alternatively, pass `allowFullScan: true` to permit an unconditional ordered scan, or omit `sort` entirely to iterate without an index requirement.
1919
-
1920
- 8. **Debug query planning with `explain` and `enforceExecutionOrder`**:
1965
+ 8. **Debug with `explain` and `enforceExecutionOrder`**:
1921
1966
  - `explain: true` — returns conditions reordered as Harper will execute them.
1922
- - `enforceExecutionOrder: true` — forces conditions to execute in the order supplied, disabling automatic re-ordering.
1967
+ - `enforceExecutionOrder: true` — forces conditions to execute in the order supplied, disabling Harper's automatic re-ordering.
1923
1968
 
1924
- 9. **Use `addTo` for concurrent-safe numeric updates**: `addTo` uses CRDT incrementation, safe across threads and nodes.
1969
+ 9. **Use `addTo` for concurrent-safe increments**: `addTo(property, value)` uses CRDT incrementation, safe across threads and nodes.
1925
1970
 
1926
1971
  ```javascript
1927
- const record = await Product.update(32);
1928
- record.addTo('quantity', -1);
1972
+ static async post(target, data) {
1973
+ const record = await this.update(target.id);
1974
+ record.addTo('quantity', -1);
1975
+ }
1929
1976
  ```
1930
1977
 
1931
- 10. **Wrap background work in `transaction()`**: HTTP handlers get a transaction automatically. Use `transaction()` explicitly for timers, background jobs, or any code outside a request context. Always `await` the call and `catch` errors.
1978
+ 10. **Wrap background work in `transaction()`**: Harper auto-starts transactions for HTTP handlers. Use `transaction()` explicitly for timers, background jobs, or any code outside a natural transaction context.
1932
1979
 
1933
1980
  ```javascript
1934
1981
  await transaction(async (txn) => {
@@ -1940,18 +1987,18 @@ Apply this rule when writing server-side Harper component code that reads from o
1940
1987
 
1941
1988
  The `txn` object exposes:
1942
1989
 
1943
- | Member | Description |
1944
- | --------------------- | ------------------------------------------------------ |
1945
- | `commit()` | Commits the current transaction |
1946
- | `abort()` | Aborts and resets the transaction |
1947
- | `resetReadSnapshot()` | Resets the read snapshot to the latest committed state |
1948
- | `timestamp` | Timestamp associated with the current transaction |
1990
+ | Member | Type | Description |
1991
+ | --------------------- | --------------- | ------------------------------------------------------ |
1992
+ | `commit()` | `() => Promise` | Commits the current transaction |
1993
+ | `abort()` | `() => void` | Aborts the transaction and resets it |
1994
+ | `resetReadSnapshot()` | `() => void` | Resets the read snapshot to the latest committed state |
1995
+ | `timestamp` | `number` | Timestamp associated with the current transaction |
1949
1996
 
1950
- **Atomicity**: all tables in the same database share one transactional context — writes across multiple tables commit atomically. Tables in different databases each get their own transaction with no cross-database atomicity guarantee.
1997
+ On normal callback completion the transaction commits automatically. If the callback throws, the transaction is aborted.
1951
1998
 
1952
- If `transaction()` is called with a context that already has an active transaction, it reuses that transaction — safe to call defensively.
1999
+ 11. **Understand atomicity**: Transactions span a single database. All tables within the same database share one transactional context — reads return a consistent snapshot and writes across multiple tables commit atomically. Cross-database operations get separate transactions with no cross-database atomicity guarantee.
1953
2000
 
1954
- 11. **Keep `harper` external when bundling for SSR**: In `vite.config`, mark `harper` as external so it resolves to the runtime rather than being bundled.
2001
+ 12. **Keep `harper` external when bundling for SSR**: In `vite.config`, mark `harper` as external so it resolves to the runtime rather than being bundled.
1955
2002
 
1956
2003
  ```javascript
1957
2004
  // vite.config
@@ -1962,7 +2009,7 @@ Apply this rule when writing server-side Harper component code that reads from o
1962
2009
 
1963
2010
  #### Examples
1964
2011
 
1965
- ##### Full CRUD sequence
2012
+ **Full CRUD flow:**
1966
2013
 
1967
2014
  ```javascript
1968
2015
  import { tables } from 'harper';
@@ -1974,19 +2021,18 @@ const created = await Product.create({ name: 'Shirt', price: 9.5 });
1974
2021
  // Patch
1975
2022
  await Product.patch(created.id, { price: Math.round(created.price * 0.8 * 100) / 100 });
1976
2023
 
1977
- // Retrieve by primary key
2024
+ // Get by primary key
1978
2025
  const record = await Product.get(created.id);
1979
2026
 
1980
- // Query with conditions
1981
- const query = {
2027
+ // Search with conditions
2028
+ for await (const record of Product.search({
1982
2029
  conditions: [{ attribute: 'price', comparator: 'less_than', value: 8.0 }],
1983
- };
1984
- for await (const record of Product.search(query)) {
2030
+ })) {
1985
2031
  // process record
1986
2032
  }
1987
2033
  ```
1988
2034
 
1989
- ##### Nested conditions with `or`
2035
+ **Nested conditions with `or`:**
1990
2036
 
1991
2037
  ```javascript
1992
2038
  Product.search({
@@ -2003,13 +2049,13 @@ Product.search({
2003
2049
  });
2004
2050
  ```
2005
2051
 
2006
- ##### Chained attribute reference (join/relationship)
2052
+ **Chained attribute reference (join):**
2007
2053
 
2008
2054
  ```javascript
2009
2055
  Product.search({ conditions: [{ attribute: ['brand', 'name'], value: 'Harper' }] });
2010
2056
  ```
2011
2057
 
2012
- ##### Background job with `transaction()`
2058
+ **Background job with `transaction()`:**
2013
2059
 
2014
2060
  ```javascript
2015
2061
  import { isMainThread } from 'node:worker_threads';
@@ -2037,7 +2083,7 @@ if (isMainThread) {
2037
2083
  }
2038
2084
  ```
2039
2085
 
2040
- ##### SSR render with `tables`
2086
+ **SSR with Harper data:**
2041
2087
 
2042
2088
  ```typescript
2043
2089
  import { tables } from 'harper';
@@ -2050,10 +2096,12 @@ export async function render(url: string): Promise<string> {
2050
2096
 
2051
2097
  #### Notes
2052
2098
 
2053
- - `tables` and `databases` do **not** automatically apply role permissions — calls run in a trusted server-side context. Apply your own authorization controls before exposing results.
2054
- - Destructive operations (`update`, `patch`, `delete`) act on live data and are not easily reversible. Scope them with specific `conditions` and validate the affected set before writing.
2055
- - `tables` is the same live, process-wide object whether accessed as a global or via `import { tables } from 'harper'`. A record written through one component is immediately visible to every other.
2056
- - Run `npm link harper` for components in their own directory to ensure typings match the running installation.
2099
+ - `tables` and `databases` calls run in a trusted server-side context and do **not** automatically apply the target table's role permissions.
2100
+ - Programmatic `update`, `patch`, and `delete` calls operate directly on stored data. Scope destructive operations with specific `conditions`, validate the affected set before writing, and gate them behind authorization controls.
2101
+ - If `transaction()` is called with a context that already has an active transaction, it reuses that transaction — making it safe to call defensively.
2102
+ - Always `await` the `transaction()` call and `catch` errors when outside a request context; an unawaited call means a failed write is never observed.
2103
+ - Guard against timer overlap: if a job can outlast its interval, use a `running` flag to skip a tick rather than opening two transactions over the same rows.
2104
+ - CommonJS is also supported: `const { tables, Resource } = require('harper');`
2057
2105
 
2058
2106
  ### 3.4 TypeScript Type Stripping in Harper
2059
2107
 
@@ -2276,18 +2324,20 @@ Instructions for the agent to follow when deploying a Harper application to a re
2276
2324
 
2277
2325
  #### When to Use
2278
2326
 
2279
- Apply this rule when deploying a Harper application to a remote Harper instance or Fabric cluster, including first-time deploys, redeployments, rollbacks, and CI/CD pipeline deployments. Also apply it when provisioning credentials for private repository deploys. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
2327
+ Apply this rule when deploying a Harper application to a remote Harper Fabric cluster, whether from a local machine or a CI/CD pipeline. Use it to configure authentication, select a package source, and run `harper deploy` with the correct parameters.
2328
+
2329
+ See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) to set up a cluster before deploying.
2280
2330
 
2281
2331
  #### How It Works
2282
2332
 
2283
- 1. **Authenticate against the remote cluster**: Run `harper login` once, pointing at the cluster's Application URL (found on the cluster's **Config → Overview** page). The CLI stores the token and writes `HARPER_CLI_TARGET` to a local `.env`.
2333
+ 1. **Authenticate against the cluster**: Run `harper login` once, pointing at the cluster's Application URL (found on the cluster's **Config → Overview** page). The CLI stores the token so subsequent commands need no credentials. If the current directory already has a `.env` file that sets no target, it also appends `HARPER_CLI_TARGET` to it.
2284
2334
 
2285
2335
  ```bash
2286
2336
  harper login <Application URL>
2287
2337
  # Provide cluster username and password when prompted
2288
2338
  ```
2289
2339
 
2290
- 2. **Deploy the application**: After login, run `harper deploy` without repeating credentials. Use `restart=true` and `replicated=true` for production deploys.
2340
+ 2. **Deploy the application**: After logging in, run `harper deploy` without repeating credentials. Omit `package` to package and deploy the current local directory.
2291
2341
 
2292
2342
  ```bash
2293
2343
  harper deploy \
@@ -2298,16 +2348,16 @@ Apply this rule when deploying a Harper application to a remote Harper instance
2298
2348
  replicated=true
2299
2349
  ```
2300
2350
 
2301
- 3. **Choose a package source**: Set the `package` parameter to any valid npm dependency value. Options:
2351
+ 3. **Choose a package source**: Set `package` to any valid npm dependency value.
2302
2352
 
2303
- | Source | Example value |
2304
- | ----------------------- | ------------------------------------------------------------ |
2305
- | Current local directory | Omit `package` |
2306
- | npm package | `package="@harperdb/status-check"` |
2307
- | GitHub (shorthand) | `package="HarperFast/status-check"` |
2308
- | GitHub (URL) | `package="https://github.com/HarperFast/status-check"` |
2309
- | Private repo (SSH) | `package="git+ssh://git@github.com:HarperDB/secret-app.git"` |
2310
- | Tarball | `package="https://example.com/application.tar.gz"` |
2353
+ | Value | Meaning |
2354
+ | ------------------------------------------------------------ | ---------------------------------------------- |
2355
+ | _(omit)_ | Package and deploy the current local directory |
2356
+ | `package="@harperdb/status-check"` | npm package |
2357
+ | `package="HarperFast/status-check"` | GitHub shorthand |
2358
+ | `package="https://github.com/HarperFast/status-check"` | GitHub URL |
2359
+ | `package="git+ssh://git@github.com:HarperDB/secret-app.git"` | Private repo (SSH) |
2360
+ | `package="https://example.com/application.tar.gz"` | Tarball |
2311
2361
 
2312
2362
  When using git tags, use the `semver` directive:
2313
2363
 
@@ -2315,68 +2365,55 @@ Apply this rule when deploying a Harper application to a remote Harper instance
2315
2365
  HarperFast/application-template#semver:v1.0.0
2316
2366
  ```
2317
2367
 
2318
- 4. **Deploy by reference (pinned commit)**: Use `by_ref=true` to send a pinned git reference instead of uploading a snapshot. The cluster fetches and builds from that exact commit SHA.
2368
+ 4. **Deploy by reference** (pinned commit): Use `by_ref=true` to send a pinned git reference instead of uploading a snapshot. The cluster fetches and builds that exact commit.
2319
2369
 
2320
2370
  ```bash
2321
2371
  harper deploy by_ref=true restart=true replicated=true
2322
2372
  ```
2323
2373
 
2324
2374
  - `by_ref` — Build the package reference from the local repository.
2325
- - `ref` _(optional)_ — Deploy a specific commit, tag, or branch instead of `HEAD`. Implies `by_ref`. Tags and branches in `refs/tags` and `refs/heads` namespaces are resolved to a full commit SHA before the deploy is sent.
2375
+ - `ref` _(optional)_ — Deploy a specific commit, tag, or branch instead of `HEAD`. Implies `by_ref`. Resolved to a full commit SHA before being sent.
2326
2376
  - `credential` _(optional)_ — Set to `true` to authenticate the clone with the stored credential for the repository's host. Omit for public repositories.
2327
2377
 
2378
+ Deploy a specific tag or commit:
2379
+
2328
2380
  ```bash
2329
- # Deploy a specific tag
2330
2381
  harper deploy ref=v1.2.0 restart=true replicated=true
2331
-
2332
- # Roll back by deploying an older commit
2333
2382
  harper deploy ref=9f8c2a1 restart=true replicated=true
2334
2383
  ```
2335
2384
 
2336
- **Important constraints on refs:**
2337
- - A full commit SHA is accepted directly with no resolution.
2338
- - Every other `ref` must resolve to something a clone can fetch: `refs/tags/*` or `refs/heads/*`, or a bare branch or tag name.
2339
- - Qualified refs outside those two namespaces (e.g., `refs/pull/123/head`) are rejected.
2340
- - Commit and push before deploying — the cluster clones from the remote and only sees pushed commits.
2341
- - Run `git fetch` if a ref can't be resolved, or pass a full commit SHA.
2385
+ Valid `ref` values must resolve to `refs/heads/*` or `refs/tags/*`, or a bare branch or tag name. Qualified refs outside those two namespaces (e.g. `refs/pull/123/head`) are rejected. A full commit SHA is always accepted directly with no resolution attempted. Run `git fetch` if a ref can't be resolved, or pass a full commit SHA.
2342
2386
 
2343
- 5. **Handle private repositories**: Pass `credential=true` so the CLI attaches a credentials reference that the cluster resolves in memory at clone time. No token travels in the operation body or lands on disk.
2387
+ **Commit and push before deploying by reference.** The cluster clones from the remote and only sees pushed commits.
2388
+
2389
+ 5. **Roll back to a previous release**: Activate a kept release by its `deployment_id` to restore the exact installed release without a fetch, rebuild, or reinstall.
2344
2390
 
2345
2391
  ```bash
2346
- harper deploy by_ref=true credential=true restart=true replicated=true
2392
+ harper deploy project=<name> deployment_id=<id> restart=true
2347
2393
  ```
2348
2394
 
2349
- Provision the credential once with `setup=true` before using `credential=true`.
2350
-
2351
- 6. **Provision a deploy credential**: Run `harper deploy setup=true` once per component and source to provision credentials for private deploys. This operation requires **super_user** — run it with an administrative credential, not the CI identity.
2395
+ 6. **Provision a deploy credential for private repositories**: Run `harper deploy setup=true` once per component and source. This requires **super_user** privileges — run it with an administrative credential.
2352
2396
 
2353
2397
  ```bash
2354
2398
  harper deploy setup=true
2355
2399
  ```
2356
2400
 
2357
- This interactive command:
2401
+ The setup flow:
2358
2402
  1. Fetches the cluster's public key with `get_secrets_public_key`.
2359
2403
  2. Encrypts the token locally into an `enc:v1:` envelope.
2360
- 3. Stores only the ciphertext with `set_secret`, in the component-scoped tier.
2404
+ 3. Stores only the ciphertext with `set_secret`, scoped to the component.
2361
2405
  4. Grants the component permission to resolve it with `grant_secret`.
2362
2406
  5. Prints the `credentials` reference for the deploy to use.
2363
2407
 
2364
- **Use a fine-grained PAT** for GitHub repositories. The prompt defaults to a fine-grained personal access token with **Contents: Read-only** on that one repository. If you use the `gh` CLI session token instead, it typically carries `read:org`, `repo`, `gist`, and `workflow` scopes across your whole account — the CLI prints a warning if you choose it. Use the narrowest credential that does the job, because the stored token is replayed on every cold deploy and rollback.
2408
+ The plaintext never leaves your machine. Prefer a **fine-grained** personal access token with **Contents: Read-only** on the target repository. Avoid broad session tokens — they typically carry `read:org`, `gist`, and `workflow` scopes across your whole account.
2365
2409
 
2366
- 7. **Use environment variables for CI/CD**: Instead of `harper login`, export credentials as environment variables.
2410
+ 7. **Deploy from a private repository**: Pass `credential=true` after provisioning the deploy credential.
2367
2411
 
2368
2412
  ```bash
2369
- export HARPER_CLI_USERNAME=<username>
2370
- export HARPER_CLI_PASSWORD=<password>
2371
- harper deploy \
2372
- project=<name> \
2373
- package=<package> \
2374
- target=<remote> \
2375
- restart=true \
2376
- replicated=true
2413
+ harper deploy by_ref=true credential=true restart=true replicated=true
2377
2414
  ```
2378
2415
 
2379
- 8. **Use dedicated auth parameters for one-off commands** (not recommended for production): Pass `auth_username` and `auth_password` directly. These take precedence over environment variables and saved login tokens.
2416
+ 8. **Use one-off authentication parameters** (not recommended for production): Pass `auth_username` and `auth_password` directly. These take precedence over environment variables and saved login tokens.
2380
2417
 
2381
2418
  ```bash
2382
2419
  harper deploy \
@@ -2389,65 +2426,61 @@ Apply this rule when deploying a Harper application to a remote Harper instance
2389
2426
  replicated=true
2390
2427
  ```
2391
2428
 
2429
+ 9. **Use CI/CD pipelines**: For GitHub Actions, use workload identity (OIDC) so no Harper credential is stored in the pipeline. Alternatively, supply a refresh token from `harper login --for-ci`. On a `pull_request` run, pass the head SHA explicitly:
2430
+
2431
+ ```bash
2432
+ harper deploy ref=${{ github.event.pull_request.head.sha }} restart=true replicated=true
2433
+ ```
2434
+
2392
2435
  #### Examples
2393
2436
 
2394
- **Standard deploy after login:**
2437
+ **Login and deploy the current directory to a remote cluster:**
2395
2438
 
2396
2439
  ```bash
2397
2440
  harper login https://my-cluster.harperdbcloud.com
2398
2441
  harper deploy \
2399
2442
  project=my-app \
2400
- package="HarperFast/my-app" \
2401
2443
  target=https://my-cluster.harperdbcloud.com \
2402
2444
  restart=true \
2403
2445
  replicated=true
2404
2446
  ```
2405
2447
 
2406
- **Deploy by reference with a tag:**
2448
+ **Deploy a specific GitHub repository by tag:**
2407
2449
 
2408
2450
  ```bash
2409
- harper deploy ref=v1.2.0 restart=true replicated=true
2451
+ harper deploy \
2452
+ project=my-app \
2453
+ package="HarperFast/application-template#semver:v1.0.0" \
2454
+ target=https://my-cluster.harperdbcloud.com \
2455
+ restart=true \
2456
+ replicated=true
2410
2457
  ```
2411
2458
 
2412
- **Deploy a private GitHub repo by reference:**
2459
+ **Deploy by reference from a private repository after provisioning credentials:**
2413
2460
 
2414
2461
  ```bash
2415
- # Provision credential once (requires super_user)
2462
+ # Provision once (requires super_user)
2416
2463
  harper deploy setup=true
2417
2464
 
2418
- # Deploy using stored credential
2465
+ # Deploy by reference with credential
2419
2466
  harper deploy by_ref=true credential=true restart=true replicated=true
2420
2467
  ```
2421
2468
 
2422
- **GitHub Actions — pull request deploy:**
2469
+ **Roll back to a previous deployment:**
2423
2470
 
2424
2471
  ```bash
2425
- harper deploy ref=${{ github.event.pull_request.head.sha }} restart=true replicated=true
2426
- ```
2427
-
2428
- **CI/CD deploy using environment variables:**
2429
-
2430
- ```bash
2431
- export HARPER_CLI_USERNAME=<username>
2432
- export HARPER_CLI_PASSWORD=<password>
2433
- harper deploy \
2434
- project=my-app \
2435
- package="@myorg/my-app" \
2436
- target=https://my-cluster.harperdbcloud.com \
2437
- restart=true \
2438
- replicated=true
2472
+ harper deploy project=my-app deployment_id=<id> restart=true
2439
2473
  ```
2440
2474
 
2441
2475
  #### Notes
2442
2476
 
2443
- - The cluster's Application URL is found on the **Config → Overview** page of the Fabric dashboard.
2444
- - `harper deploy setup=true` calls `get_secrets_public_key`, `set_secret`, and `grant_secret`, all of which require **super_user**. Do not run it with the CI identity.
2445
- - The `enc:v1:` envelope means the plaintext token never leaves your machine — only ciphertext is stored and replicated.
2446
- - Secrets are stored scoped to the component, not in the global `processEnv` tier. If a global secret already exists at the derived name, it is converted to the scoped tier.
2477
+ - The cluster's Application URL is found on the **Config → Overview** page.
2478
+ - `harper deploy setup=true` requires **super_user** — run it with an administrative credential, not the CI identity it provisions for.
2479
+ - Secrets are stored scoped to the component, never in the global `processEnv` tier. The `enc:v1:` envelope is the only form of the token that ever leaves your machine or travels over the operations API.
2447
2480
  - For SSH-based private repos, use the `add_ssh_key` operation to register keys before deploying.
2448
- - If your application requires a build step that cannot run on the cluster node, deploy a built payload (omit `by_ref`) instead of deploying by reference.
2481
+ - A `ref` pointing to `refs/tags` is resolved to a full commit SHA before the deploy is sent. Tags that move mid-deploy could otherwise leave cluster nodes running different code.
2449
2482
  - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
2450
- - Annotated tags in `refs/tags` resolve to the commit they point at, not the tag object.
2483
+ - Deploying by reference means the cluster installs and builds from source. If your application requires a build step that cannot run on the node, deploy a built payload instead.
2451
2484
 
2452
2485
  ### 4.2 Creating a Harper Fabric Account and Cluster
2453
2486