create-harper 1.2.2 → 1.3.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.
Files changed (90) hide show
  1. package/lib/install.js +5 -0
  2. package/package.json +1 -1
  3. package/template-react/README.md +1 -1
  4. package/template-react/package.json +2 -0
  5. package/template-react/resources/README.md +3 -3
  6. package/template-react/schemas/README.md +2 -2
  7. package/template-react-ts/README.md +1 -1
  8. package/template-react-ts/package.json +2 -0
  9. package/template-react-ts/resources/README.md +3 -3
  10. package/template-react-ts/schemas/README.md +2 -2
  11. package/template-vanilla/README.md +1 -1
  12. package/template-vanilla/package.json +2 -0
  13. package/template-vanilla/resources/README.md +3 -3
  14. package/template-vanilla/schemas/README.md +2 -2
  15. package/template-vanilla-ts/README.md +1 -1
  16. package/template-vanilla-ts/package.json +2 -0
  17. package/template-vanilla-ts/resources/README.md +3 -3
  18. package/template-vanilla-ts/schemas/README.md +2 -2
  19. package/template-react/AGENTS.md +0 -22
  20. package/template-react/skills/adding-tables-with-schemas.md +0 -34
  21. package/template-react/skills/automatic-apis.md +0 -53
  22. package/template-react/skills/automatic-rest-apis.md +0 -41
  23. package/template-react/skills/caching.md +0 -113
  24. package/template-react/skills/checking-authentication.md +0 -281
  25. package/template-react/skills/custom-resources.md +0 -86
  26. package/template-react/skills/defining-relationships.md +0 -71
  27. package/template-react/skills/deploying-to-harper-fabric.md +0 -20
  28. package/template-react/skills/extending-tables.md +0 -70
  29. package/template-react/skills/handling-binary-data.md +0 -67
  30. package/template-react/skills/programmatic-table-requests.md +0 -185
  31. package/template-react/skills/querying-rest-apis.md +0 -69
  32. package/template-react/skills/real-time-apps.md +0 -75
  33. package/template-react/skills/serving-web-content.md +0 -82
  34. package/template-react/skills/typescript-type-stripping.md +0 -47
  35. package/template-react/skills/using-blob-datatype.md +0 -131
  36. package/template-react/skills/vector-indexing.md +0 -215
  37. package/template-react-ts/AGENTS.md +0 -22
  38. package/template-react-ts/skills/adding-tables-with-schemas.md +0 -34
  39. package/template-react-ts/skills/automatic-apis.md +0 -53
  40. package/template-react-ts/skills/automatic-rest-apis.md +0 -41
  41. package/template-react-ts/skills/caching.md +0 -113
  42. package/template-react-ts/skills/checking-authentication.md +0 -281
  43. package/template-react-ts/skills/custom-resources.md +0 -86
  44. package/template-react-ts/skills/defining-relationships.md +0 -71
  45. package/template-react-ts/skills/deploying-to-harper-fabric.md +0 -20
  46. package/template-react-ts/skills/extending-tables.md +0 -70
  47. package/template-react-ts/skills/handling-binary-data.md +0 -67
  48. package/template-react-ts/skills/programmatic-table-requests.md +0 -185
  49. package/template-react-ts/skills/querying-rest-apis.md +0 -69
  50. package/template-react-ts/skills/real-time-apps.md +0 -75
  51. package/template-react-ts/skills/serving-web-content.md +0 -82
  52. package/template-react-ts/skills/typescript-type-stripping.md +0 -47
  53. package/template-react-ts/skills/using-blob-datatype.md +0 -131
  54. package/template-react-ts/skills/vector-indexing.md +0 -215
  55. package/template-vanilla/AGENTS.md +0 -22
  56. package/template-vanilla/skills/adding-tables-with-schemas.md +0 -34
  57. package/template-vanilla/skills/automatic-apis.md +0 -53
  58. package/template-vanilla/skills/automatic-rest-apis.md +0 -41
  59. package/template-vanilla/skills/caching.md +0 -113
  60. package/template-vanilla/skills/checking-authentication.md +0 -281
  61. package/template-vanilla/skills/custom-resources.md +0 -86
  62. package/template-vanilla/skills/defining-relationships.md +0 -71
  63. package/template-vanilla/skills/deploying-to-harper-fabric.md +0 -20
  64. package/template-vanilla/skills/extending-tables.md +0 -70
  65. package/template-vanilla/skills/handling-binary-data.md +0 -67
  66. package/template-vanilla/skills/programmatic-table-requests.md +0 -185
  67. package/template-vanilla/skills/querying-rest-apis.md +0 -69
  68. package/template-vanilla/skills/real-time-apps.md +0 -75
  69. package/template-vanilla/skills/serving-web-content.md +0 -82
  70. package/template-vanilla/skills/typescript-type-stripping.md +0 -47
  71. package/template-vanilla/skills/using-blob-datatype.md +0 -131
  72. package/template-vanilla/skills/vector-indexing.md +0 -215
  73. package/template-vanilla-ts/AGENTS.md +0 -22
  74. package/template-vanilla-ts/skills/adding-tables-with-schemas.md +0 -34
  75. package/template-vanilla-ts/skills/automatic-apis.md +0 -53
  76. package/template-vanilla-ts/skills/automatic-rest-apis.md +0 -41
  77. package/template-vanilla-ts/skills/caching.md +0 -113
  78. package/template-vanilla-ts/skills/checking-authentication.md +0 -281
  79. package/template-vanilla-ts/skills/custom-resources.md +0 -86
  80. package/template-vanilla-ts/skills/defining-relationships.md +0 -71
  81. package/template-vanilla-ts/skills/deploying-to-harper-fabric.md +0 -20
  82. package/template-vanilla-ts/skills/extending-tables.md +0 -70
  83. package/template-vanilla-ts/skills/handling-binary-data.md +0 -67
  84. package/template-vanilla-ts/skills/programmatic-table-requests.md +0 -185
  85. package/template-vanilla-ts/skills/querying-rest-apis.md +0 -69
  86. package/template-vanilla-ts/skills/real-time-apps.md +0 -75
  87. package/template-vanilla-ts/skills/serving-web-content.md +0 -82
  88. package/template-vanilla-ts/skills/typescript-type-stripping.md +0 -47
  89. package/template-vanilla-ts/skills/using-blob-datatype.md +0 -131
  90. package/template-vanilla-ts/skills/vector-indexing.md +0 -215
@@ -1,185 +0,0 @@
1
- # Programmatic Requests with Harper Tables
2
-
3
- In Harper, you can interact with your database tables programmatically using the global `tables` object. Each table defined in your schema is available as a property on this object.
4
-
5
- ## Basic Usage
6
-
7
- The `tables` object provides a direct way to perform CRUD operations from within your Harper resources or scripts.
8
-
9
- ```typescript
10
- const track = await tables.ExamplePeople.get(id) as ExamplePerson;
11
- ```
12
-
13
- ## Available Methods
14
-
15
- The following methods are available on each table object:
16
-
17
- ### `get(idOrQuery)`
18
-
19
- Retrieves a single record by ID or multiple records matching a query.
20
-
21
- - **By ID**:
22
- ```typescript
23
- const person = await tables.ExamplePeople.get('person-123');
24
- ```
25
- - **By Query**:
26
- ```typescript
27
- const albums = await tables.ExamplePeople.get({
28
- conditions: [{ attribute: 'name', value: 'John' }],
29
- });
30
- ```
31
-
32
- ### `put(id, record)`
33
-
34
- Replaces an entire record with the provided data. If the record doesn't exist, it will be created.
35
-
36
- ```typescript
37
- await tables.ExamplePeople.put('person-123', {
38
- name: 'Michael Jackson',
39
- tag: 'entertainer',
40
- });
41
- ```
42
-
43
- ### `patch(id, partialRecord)`
44
-
45
- Performs a partial update on a record. Only the specified fields will be updated.
46
-
47
- ```typescript
48
- await tables.ExamplePeople.patch('person-123', {
49
- tag: 'tragedy',
50
- });
51
- ```
52
-
53
- ### `delete(idOrQuery)`
54
-
55
- Deletes a record by ID or multiple records matching a query.
56
-
57
- ```typescript
58
- await tables.ExamplePeople.delete('person-123');
59
- ```
60
-
61
- ### `post(record)`
62
-
63
- Creates a new record. Use this when you want the database to auto-generate a primary key.
64
-
65
- ```typescript
66
- const newAlbum = await tables.ExamplePeople.post({
67
- name: 'John Smith',
68
- tag: 'anonymous',
69
- });
70
- ```
71
-
72
- ### `update(id, updates)`
73
-
74
- The `update` method is a versatile tool for modifying records. While it can perform simple partial updates like `patch`, its primary power lies in its ability to return an **Updatable Record** object for complex or atomic operations.
75
-
76
- #### Partial Update (like `patch`)
77
-
78
- When called with both an ID and an update object, it performs a partial update:
79
-
80
- ```typescript
81
- await tables.ExamplePeople.update('person-123', {
82
- name: 'Updated Name',
83
- });
84
- ```
85
-
86
- #### Getting an Updatable Record
87
-
88
- When called without an update object (only an ID), `update` returns a reference to the record that can be modified directly.
89
-
90
- ```typescript
91
- const person = await tables.ExamplePeople.update('person-123');
92
- person.name = 'New Person Name';
93
- // Properties can be assigned directly and are tracked
94
- ```
95
-
96
- #### Atomic Operations
97
-
98
- Updatable records provide methods for safe, atomic modifications, which are essential for avoiding race conditions during increments or decrements:
99
-
100
- - `addTo(attribute, value)`: Atomically adds to a numeric value.
101
- - `subtractFrom(attribute, value)`: Atomically subtracts from a numeric value.
102
-
103
- ```typescript
104
- const stats = await tables.Stats.update('daily');
105
- stats.addTo('viewCount', 1);
106
- ```
107
-
108
- #### Update without an ID or with Context
109
-
110
- Calling `update()` without an ID can be used when the target is implied by the context (e.g., inside a resource method) or when you want to get an updatable reference to a collection.
111
-
112
- ```typescript
113
- // Inside a custom resource method
114
- const record = await this.update();
115
- record.set('status', 'processed');
116
-
117
- // Passing specific context (like a transaction)
118
- const person = await tables.ExamplePeople.update('person-123', null, {
119
- transaction,
120
- });
121
- person.addTo('playCount', 1);
122
- ```
123
-
124
- ### `search(query)`
125
-
126
- Performs a search based on the provided query criteria. It returns an `AsyncIterable` which is efficient for streaming large result sets.
127
-
128
- ```typescript
129
- const results = await tables.ExamplePeople.search({
130
- conditions: [{ attribute: 'artist', value: 'Michael Jackson' }],
131
- });
132
-
133
- for await (const person of results) {
134
- console.log(person.name);
135
- }
136
- ```
137
-
138
- ### `subscribe(query)`
139
-
140
- Subscribes to real-time changes in the table. You can provide a query to filter which changes trigger a notification.
141
-
142
- ```typescript
143
- const subscription = await tables.ExamplePeople.subscribe({
144
- conditions: [{ attribute: 'name', value: 'Thriller' }],
145
- });
146
-
147
- for await (const event of subscription) {
148
- console.log('Record changed:', event.value);
149
- }
150
- ```
151
-
152
- ### `publish(id, message)`
153
-
154
- Publishes a message to a specific record or topic. This triggers any active subscriptions without necessarily persisting data to the table.
155
-
156
- ```typescript
157
- await tables.ExamplePeople.publish('person-123', {
158
- type: 'REVALIDATE',
159
- timestamp: Date.now(),
160
- });
161
- ```
162
-
163
- ## Querying Options
164
-
165
- Many methods accept a `Query` (or `RequestTarget`) object. Common options include:
166
-
167
- - `conditions`: Array of filter conditions.
168
- - `limit`: Number of records to return.
169
- - `offset`: Number of records to skip.
170
- - `sort`: Attribute and direction for sorting.
171
- - `select`: Array of specific attributes to return.
172
-
173
- Example of a complex query:
174
-
175
- ```typescript
176
- const recentAlbums = await tables.ExamplePeople.get({
177
- conditions: [{
178
- attribute: 'releaseDate',
179
- comparator: 'ge',
180
- value: '2020-01-01',
181
- }],
182
- sort: { attribute: 'releaseDate', descending: true },
183
- limit: 10,
184
- });
185
- ```
@@ -1,69 +0,0 @@
1
- # Querying through REST APIs in Harper
2
-
3
- Harper's automatic REST APIs support powerful querying capabilities directly through URL query parameters. This allows you to filter, sort, paginate, and join data without writing complex queries.
4
-
5
- ## Basic Filtering
6
-
7
- The simplest way to filter is by using attribute names as query parameters:
8
-
9
- `GET /ExamplePeople/?tag=friend`
10
-
11
- This returns all records where `tag` equals `friend`.
12
-
13
- ## Comparison Operators (FIQL-style)
14
-
15
- You can use standard comparison operators by appending them to the attribute name with an `=` sign:
16
-
17
- - `gt`: Greater than
18
- - `ge`: Greater than or equal to
19
- - `lt`: Less than
20
- - `le`: Less than or equal to
21
- - `ne`: Not equal
22
-
23
- Example:
24
- `GET /Products/?price=gt=100&price=lt=200`
25
-
26
- ## Logic Operators
27
-
28
- - **AND**: Use the `&` operator (default).
29
- - **OR**: Use the `|` operator.
30
-
31
- Example:
32
- `GET /Products/?rating=5|featured=true`
33
-
34
- ## Grouping
35
-
36
- Use parentheses `()` to group conditions and indicate order of operations.
37
-
38
- Example:
39
- `GET /Products/?(rating=5|featured=true)&price=lt=50`
40
-
41
- ## Selection
42
-
43
- Use `select()` to limit the returned fields:
44
-
45
- `GET /Products/?select(name,price)`
46
-
47
- ## Pagination
48
-
49
- Use `limit(start, end)` or `limit(end)`:
50
-
51
- - `limit(10)`: Returns the first 10 records.
52
- - `limit(20, 10)`: Skips the first 20 records and returns the next 10.
53
-
54
- Example:
55
- `GET /Products/?limit(0,20)`
56
-
57
- ## Sorting
58
-
59
- Use `sort()` with `+` (ascending) or `-` (descending) prefixes:
60
-
61
- `GET /Products/?sort(+price,-rating)`
62
-
63
- ## Joins and Chained Attributes
64
-
65
- If you have defined relationships in your schema using the `@relationship` directive, you can use dot syntax to query across tables. For more on defining these, see the [Defining Relationships](defining-relationships.md) skill.
66
-
67
- `GET /Book/?author.name=Harper`
68
-
69
- This will perform an automatic join and filter books based on the related author's name.
@@ -1,75 +0,0 @@
1
- # Real-time Applications in Harper
2
-
3
- Harper provides built-in support for real-time data synchronization using WebSockets and a Pub/Sub mechanism. This allows clients to receive immediate updates when data changes in the database.
4
-
5
- ## Automatic WebSockets
6
-
7
- For many use cases, the [Automatic APIs](automatic-apis.md) provided by Harper are more than enough. When you `@export` a table, Harper automatically provides a WebSocket endpoint that publishes events whenever data in that table is updated.
8
-
9
- ## Implementing a WebSocket Resource
10
-
11
- Customizing resources by implementing a `connect` method is only necessary when you want to come up with a more specific back-and-forth or custom message handling. To handle WebSocket connections, implement the `connect` method in your custom resource class.
12
-
13
- ### Example: `resources/exampleSocket.ts`
14
-
15
- ```typescript
16
- import {
17
- type IterableEventQueue,
18
- RequestTarget,
19
- Resource,
20
- tables,
21
- } from 'harperdb';
22
-
23
- export class ExampleSocket extends Resource {
24
- async *connect(
25
- target: RequestTarget,
26
- incomingMessages: IterableEventQueue<any>,
27
- ): AsyncIterable<any> {
28
- // Subscribe to changes in a specific table
29
- const subscription = await tables.ExamplePeople.subscribe(target);
30
-
31
- if (!incomingMessages) {
32
- // Server-Sent Events (SSE) mode: only outgoing messages
33
- return subscription;
34
- }
35
-
36
- // Handle incoming messages from the client
37
- for await (let message of incomingMessages) {
38
- // Process message and optionally yield responses
39
- yield { received: message };
40
- }
41
- }
42
- }
43
- ```
44
-
45
- ## Pub/Sub with `tables.subscribe()`
46
-
47
- You can subscribe to change events on any table using the `subscribe()` method. This is typically used within the `connect` method of a resource to stream updates to a connected client.
48
-
49
- - `tables.TableName.subscribe(target)`: Subscribes to all changes in the specified table.
50
- - The `target` parameter can include filters to only subscribe to a subset of changes.
51
-
52
- ## Server-Sent Events (SSE)
53
-
54
- If the client connects using a protocol that only supports one-way communication from the server (like standard SSE), the `incomingMessages` parameter will be null. Your `connect` method should handle this by only returning the subscription or yielding messages.
55
-
56
- ## Using WebSockets from the Client
57
-
58
- You can connect to your real-time resource using a standard WebSocket client.
59
-
60
- ```javascript
61
- const socket = new WebSocket('ws://your-harper-instance/ExampleSocket');
62
-
63
- socket.onmessage = (event) => {
64
- const data = JSON.parse(event.data);
65
- console.log('Real-time update:', data);
66
- };
67
-
68
- socket.send(JSON.stringify({ type: 'ping' }));
69
- ```
70
-
71
- ## Key Real-time Features
72
-
73
- - **Automatic Table Subscriptions**: Easily stream changes from any database table.
74
- - **Bi-directional Communication**: Send and receive messages in real-time.
75
- - **Scalable Pub/Sub**: Harper handles the efficient distribution of messages to subscribers.
@@ -1,82 +0,0 @@
1
- # Serving Web Content with Harper
2
-
3
- Harper provides two primary ways to include and serve HTTP web content such as HTML, CSS, JavaScript, and React applications. These methods are mutually exclusive.
4
-
5
- ## 1. Using the Static Plugin
6
-
7
- The `static` plugin is the simplest way to serve static files. It maps a directory on your filesystem to your Harper REST endpoint.
8
-
9
- ### Configuration
10
-
11
- Add the `static` configuration to your `config.yaml`:
12
-
13
- ```yaml
14
- static:
15
- files: 'web/*'
16
- ```
17
-
18
- ### Usage
19
-
20
- 1. Create a `web` folder in your project root.
21
- 2. Place your static files (e.g., `index.html`, `styles.css`, `app.js`) inside the `web` folder.
22
- 3. Your files will be accessible from your REST endpoint. For example, if Harper is running on `http://localhost:9926/`, your `index.html` will be available at that address.
23
-
24
- ### Key Characteristics
25
-
26
- - **Precedence**: Static files are searched first. If a matching file is found, it is served; otherwise, Harper proceeds to check your other resource and table APIs.
27
- - **No Directory Listings**: Browsing the directory structure via the browser is not supported.
28
- - **Simple Deployment**: Ideal for pre-built applications or simple static sites.
29
-
30
- ---
31
-
32
- ## 2. Using the Vite Plugin
33
-
34
- The Vite plugin integrates Vite's development server directly into Harper, providing features like Hot Module Replacement (HMR) during development.
35
-
36
- ### Configuration
37
-
38
- Add the Vite plugin to your `config.yaml`:
39
-
40
- ```yaml
41
- '@harperfast/vite-plugin':
42
- package: '@harperfast/vite-plugin'
43
- ```
44
-
45
- ### Setup
46
-
47
- This plugin expects a `vite.config.ts` and an `index.html` file in your project root. It handles rendering your app through Vite during development.
48
-
49
- ### Package Configuration
50
-
51
- To use the Vite plugin effectively, you should configure your `package.json` with the appropriate scripts and dependencies.
52
-
53
- #### Scripts
54
-
55
- Copy these script examples to manage your development and deployment workflows:
56
-
57
- ```json
58
- "scripts": {
59
- "dev": "harper run .",
60
- "build": "tsc -b && vite build",
61
- "build-and-deploy": "rm -Rf deploy && npm run build && mkdir deploy && mv web deploy/ && cp -R deploy-template/* deploy/ && dotenv -- npm run deploy-web && rm -Rf deploy",
62
- "deploy-web": "(cd deploy && harperdb deploy_component . project=web restart=rolling replicated=true)"
63
- }
64
- ```
65
-
66
- #### Dependencies and Overrides
67
-
68
- Include the Vite plugin and other related dependencies in your `devDependencies`:
69
-
70
- ```bash
71
- npm install --save-dev vite @harperfast/vite-plugin @vitejs/plugin-react dotenv-cli
72
- ```
73
-
74
- ### Deploying to Production
75
-
76
- Vite's HMR server is meant for development, not production. For that, the `build-and-deploy` script above builds the Vite app into a `web` folder, places the static handler, and then deploys that subdirectory as a Harper app to production.
77
-
78
- ### Key Characteristics
79
-
80
- - **Development Experience**: Provides Vite's HMR for a fast development cycle.
81
- - **Integrated Rendering**: Automatically handles the rendering of your React/Vite app.
82
- - **Mutual Exclusivity**: Use this approach **instead of** the static plugin if you want Vite integration.
@@ -1,47 +0,0 @@
1
- # TypeScript Type Stripping
2
-
3
- Harper supports using TypeScript directly without any additional build tools (like `tsc` or `esbuild`) by leveraging Node.js's native Type Stripping capability. This allows you to write `.ts` files for your Custom Resources and have them run directly in Harper.
4
-
5
- ## Requirements
6
-
7
- - **Node.js Version**: You must be running a version of Node.js that supports type stripping (Node.js v22.6.0 or higher).
8
- - **No Experimental Flags**: When running on supported Node.js versions, Harper can automatically handle type stripping for your resource files.
9
-
10
- ## Benefits
11
-
12
- - **Faster Development**: No need to wait for a build step or manage complex build pipelines.
13
- - **Simplified Tooling**: You don't need to install or configure `ts-node`, `tsx`, or other TypeScript execution engines for your Harper resources.
14
- - **Native Performance**: Leverages Node.js's built-in support for stripping types, which is highly efficient.
15
-
16
- ## Usage
17
-
18
- Simply name your resource files with a `.ts` extension in your `resources/` directory.
19
-
20
- ### Example: `resources/my-resource.ts`
21
-
22
- ```typescript
23
- import { Resource } from 'harperdb';
24
-
25
- export class MyResource extends Resource {
26
- async get() {
27
- return { message: 'This is running directly from TypeScript!' };
28
- }
29
- }
30
- ```
31
-
32
- When cross-referencing between modules, ensure that the file path contains the appropriate extension.
33
-
34
- ```typescript
35
- import { MyResource } from './my-resource.ts';
36
- ```
37
-
38
- ## Configuration
39
-
40
- In your `config.yaml`, ensure your `jsResource` points to your `.ts` files:
41
-
42
- ```yaml
43
- jsResource:
44
- files: 'resources/*.ts'
45
- ```
46
-
47
- When Harper starts, it will detect the `.ts` files and, if running on a compatible Node.js version, will execute them using type stripping.
@@ -1,131 +0,0 @@
1
- # Blob (Binary Large Objects)
2
-
3
- Harper supports **Blobs** — binary large objects for storing unstructured or large binary data — with integrated streaming support and efficient storage. Blobs are ideal for media files, documents, and any data where size or throughput makes standard JSON fields impractical.
4
-
5
- ---
6
-
7
- ## What Are Blobs
8
-
9
- Blobs extend the native JavaScript `Blob` type and allow you to store **binary or arbitrary data** inside Harper tables. The blob reference is stored in the record, while the blob’s contents are streamed to and from storage.
10
-
11
- - Designed for binary data such as images, audio, and documents
12
- - Supports streaming reads and writes
13
- - Blob data is stored separately from record attributes
14
- - Optimized for large payloads
15
-
16
- ---
17
-
18
- ## Defining Blob Fields
19
-
20
- Declare a blob field using the `Blob` type in your schema:
21
-
22
- ```graphql
23
- type MyTable @table {
24
- id: ID @primaryKey
25
- data: Blob
26
- }
27
- ```
28
-
29
- Any record written to this field will store a reference to the blob’s contents.
30
-
31
- ---
32
-
33
- ## Creating and Storing Blobs
34
-
35
- ### Creating a Blob from a Buffer
36
-
37
- ```js
38
- const blob = createBlob(largeBuffer);
39
- await MyTable.put({ id: 'my-record', data: blob });
40
- ```
41
-
42
- - `createBlob()` returns a blob reference
43
- - Data is streamed to storage asynchronously
44
- - Records may be committed before the blob finishes writing
45
-
46
- ---
47
-
48
- ### Creating a Blob from a Stream
49
-
50
- ```js
51
- const blob = createBlob(stream);
52
- await MyTable.put({ id: 'streamed-record', data: blob });
53
- ```
54
-
55
- Streaming allows large data to be written without loading it fully into memory.
56
-
57
- ---
58
-
59
- ## Reading Blob Data
60
-
61
- Retrieve a record and read its blob contents:
62
-
63
- ```js
64
- const record = await MyTable.get('my-record');
65
- const buffer = await record.data.bytes();
66
- ```
67
-
68
- Blob objects also support streaming interfaces for large reads.
69
-
70
- ---
71
-
72
- ## Blob Attributes and Events
73
-
74
- ### Size
75
-
76
- The blob size may not be immediately available when streaming:
77
-
78
- ```js
79
- if (blob.size === undefined) {
80
- blob.on('size', size => {
81
- console.log('Blob size:', size);
82
- });
83
- }
84
- ```
85
-
86
- ---
87
-
88
- ### saveBeforeCommit
89
-
90
- Blobs are not atomic while streaming. To ensure the blob is fully written before committing the record:
91
-
92
- ```js
93
- const blob = createBlob(stream, { saveBeforeCommit: true });
94
- await MyTable.put({ id: 'safe-record', data: blob });
95
- ```
96
-
97
- ---
98
-
99
- ## Error Handling
100
-
101
- Handle streaming errors by attaching an error listener:
102
-
103
- ```js
104
- blob.on('error', () => {
105
- MyTable.invalidate('my-record');
106
- });
107
- ```
108
-
109
- This prevents partially written blobs from being used.
110
-
111
- ---
112
-
113
- ## Automatic Coercion
114
-
115
- When a field is defined as `Blob`, assigning a string or buffer automatically converts it into a blob when using `put`, `patch`, or `publish`.
116
-
117
- ---
118
-
119
- ## Related Skill
120
-
121
- - [Handling Binary Data with Blobs](handling-binary-data.md) How to store and serve binary data like images or MP3s using the Blob data type.
122
-
123
- ---
124
-
125
- ## Summary
126
-
127
- - Blobs store large or binary data efficiently
128
- - Blob fields reference streamed content
129
- - Supports buffered and streamed writes
130
- - Optional write-before-commit behavior
131
- - Integrates seamlessly with Harper tables