diff --git a/docs/developers/applications/caching.md b/docs/developers/applications/caching.md index 46e89aa8..1f941d8a 100644 --- a/docs/developers/applications/caching.md +++ b/docs/developers/applications/caching.md @@ -86,14 +86,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -111,7 +111,7 @@ const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { if (data.invalidate) - / use this flag as a marker + // use this flag as a marker this.invalidate(); } } @@ -126,16 +126,16 @@ We can provide more control of an active cache with subscriptions. If there is a ```javascript class ThirdPartyAPI extends Resource { async *subscribe() { - setInterval(() => { / every second retrieve more data - / get the next data change event from the source + setInterval(() => { // every second retrieve more data + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change }, 1000); } async get() { @@ -166,7 +166,7 @@ By default, Harper will only run the subscribe method on one thread. Harper is m ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -219,9 +219,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -241,7 +241,7 @@ class BlogSource extends Resource { get() { const post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of post.comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/docs/developers/applications/defining-schemas.md b/docs/developers/applications/defining-schemas.md index 85d3b612..df239ca5 100644 --- a/docs/developers/applications/defining-schemas.md +++ b/docs/developers/applications/defining-schemas.md @@ -195,7 +195,7 @@ HNSW indexing finds the nearest neighbors to a search vector. To use this, you c ```javascript let results = Product.search({ sort: { attribute: 'textEmbeddings', target: searchVector }, - limit: 5, / get the five nearest neighbors + limit: 5, // get the five nearest neighbors }); ``` @@ -205,7 +205,7 @@ This can be used in combination with other conditions as well, for example: let results = Product.search({ conditions: [{ attribute: 'price', comparator: 'lt', value: 50 }], sort: { attribute: 'textEmbeddings', target: searchVector }, - limit: 5, / get the five nearest neighbors + limit: 5, // get the five nearest neighbors }); ``` diff --git a/docs/developers/applications/index.md b/docs/developers/applications/index.md index b3844498..eddd2a44 100644 --- a/docs/developers/applications/index.md +++ b/docs/developers/applications/index.md @@ -86,16 +86,16 @@ This guide is going to walk you through building a basic Harper application usin To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the Harper provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the Harper provided set of tables (in the default database) export class DogWithHumanAge extends Dog { static loadAsInstance = false; async get(target) { const record = await super.get(target); return { - ...record, / include all properties from the record - humanAge: 15 + record.age * 5, / silly calculation of human age equivalent + ...record, // include all properties from the record + humanAge: 15 + record.age * 5, // silly calculation of human age equivalent }; } } @@ -123,14 +123,14 @@ We use the new table's (static) `get()` method to retrieve a breed by id. Harper The resource methods are automatically wrapped with a transaction and will automatically commit the changes when the method finishes. This allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { static loadAsInstance = false; async get(target) { - / get the Dog record + // get the Dog record const record = await super.get(target); - / get the Breed record + // get the Breed record let breedDescription = await Breed.get(record.breed); return { ...record, @@ -168,10 +168,10 @@ export class CustomDog extends Dog { async post(target, data) { if (data.action === 'add-trick') { const context = this.getContext(); - / if we want to skip the default permission checks, we can turn off checkPermissions: + // if we want to skip the default permission checks, we can turn off checkPermissions: target.checkPermissions = false; const record = this.update(target); - / and do our own/custom permission check: + // and do our own/custom permission check: if (record.owner !== context.user?.username) { throw new Error('Can not update this record'); } @@ -186,7 +186,7 @@ Any methods that are not defined will fall back to Harper's default authorizatio You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` @@ -198,14 +198,14 @@ This will allow requests to url like / to be directly resolved to this resource. We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table +const { Breed } = tables; // our Breed table class BreedSource extends Resource { - / define a data source + // define a data source async get(target) { return (await fetch(`https://best-dog-site.com/${target}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/docs/developers/real-time.md b/docs/developers/real-time.md index 1c1ed269..9c5c79e4 100644 --- a/docs/developers/real-time.md +++ b/docs/developers/real-time.md @@ -96,7 +96,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -106,8 +106,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -121,13 +121,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -141,7 +141,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/docs/developers/replication/sharding.md b/docs/developers/replication/sharding.md index 84197445..74242292 100644 --- a/docs/developers/replication/sharding.md +++ b/docs/developers/replication/sharding.md @@ -75,7 +75,7 @@ Additionally, you can specify `replicateTo` and `replicatedConfirmation` paramet class MyTable extends tables.MyTable { put(record) { const context = this.getContext(); - context.replicateTo = 2; / or an array of node names + context.replicateTo = 2; // or an array of node names context.replicatedConfirmation = 1; return super.put(record); } @@ -132,7 +132,7 @@ Alternately you can define a custom sharding strategy based on the primary key a ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? 1 : 2; / return shard number + return id % 2 === 0 ? 1 : 2; // return shard number }); ``` @@ -140,7 +140,7 @@ or ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? ['node1'] : ['node2']; / return array of node hostnames + return id % 2 === 0 ? ['node1'] : ['node2']; // return array of node hostnames }); ``` diff --git a/docs/developers/security/users-and-roles.md b/docs/developers/security/users-and-roles.md index 9fbe6b75..7b373f59 100644 --- a/docs/developers/security/users-and-roles.md +++ b/docs/developers/security/users-and-roles.md @@ -100,17 +100,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/docs/technical-details/reference/blob.md b/docs/technical-details/reference/blob.md index 9b8871bd..a62ba2eb 100644 --- a/docs/technical-details/reference/blob.md +++ b/docs/technical-details/reference/blob.md @@ -34,7 +34,7 @@ export class MyEndpoint extends MyTable { return { status: 200, headers: {}, - body: this.data, / this.data is a blob + body: this.data, // this.data is a blob }); } } @@ -44,11 +44,11 @@ One of the important characteristics of blobs is they natively support asynchron ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is still being written to storage +// at this point the blob exists, but the data is still being written to storage await MyTable.put({ id: 'my-record', data: blob }); -/ we now have written a record that references the blob +// we now have written a record that references the blob let record = await MyTable.get('my-record'); -/ we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. +// we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. let stream = record.data.stream(); ``` @@ -57,9 +57,9 @@ Alternately, we can also wait for the blob to be fully written to storage before ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is was not been written to storage +// at this point the blob exists, but the data is was not been written to storage await blob.save(MyTable); -/ we now know the blob is fully written to storage +// we now know the blob is fully written to storage await MyTable.put({ id: 'my-record', data: blob }); ``` @@ -73,7 +73,7 @@ Because blobs can be streamed and referenced prior to their completion, there is export class MyEndpoint extends MyTable { let blob = this.data; blob.on('error', () => { - / if this was a caching table, we may want to invalidate or delete this record: + // if this was a caching table, we may want to invalidate or delete this record: this.invalidate(); }); async get() { @@ -93,11 +93,11 @@ Blobs that are created from streams may not have the standard `size` property av ```javascript let record = await MyTable.get('my-record'); let blob = record.data; -blob.size / will be available if it was saved with a known size -let stream blob.stream(); / start streaming the data +blob.size // will be available if it was saved with a known size +let stream blob.stream(); // start streaming the data if (blob.size === undefined) { blob.on('size', (size) => { - / will be called once the size is available + // will be called once the size is available }) } diff --git a/docs/technical-details/reference/components/extensions.md b/docs/technical-details/reference/components/extensions.md index f72dfdf7..e5575f8e 100644 --- a/docs/technical-details/reference/components/extensions.md +++ b/docs/technical-details/reference/components/extensions.md @@ -86,11 +86,11 @@ test-component: In order for an extension to be classified as a Resource Extension it must implement at least one of the `handleFile()`, `handleDirectory()`, `setupFile()`, or `setupDirectory()` methods. As a standalone extension, these methods should be named and exported directly. For example: ```js -/ ESM +// ESM export function handleFile() {} export function setupDirectory() {} -/ or CJS +// or CJS function handleDirectory() {} function setupFile() {} diff --git a/docs/technical-details/reference/components/plugins.md b/docs/technical-details/reference/components/plugins.md index bdc2010a..902258c1 100644 --- a/docs/technical-details/reference/components/plugins.md +++ b/docs/technical-details/reference/components/plugins.md @@ -84,7 +84,7 @@ The plugin module can export a `defaultTimeout` variable (in milliseconds) that For example: ```typescript -export const defaultTimeout = 60_000; / 60 seconds +export const defaultTimeout = 60_000; // 60 seconds ``` Additionally, users can specify a `timeout` option in their application's `config.yaml` file for a specific plugin. This option takes precedence over the plugin's `defaultTimeout` and the system default. @@ -110,7 +110,7 @@ export function handleApplication(scope) { scope.options.on('change', (key, value, config) => { if (key[0] === 'files' || key[0] === 'urlPath') { - / If the files or urlPath options change, we need to reinitialize the static files map + // If the files or urlPath options change, we need to reinitialize the static files map staticFiles.clear(); logger.info(`Static files reinitialized due to change in ${key.join('.')}`); } @@ -125,11 +125,11 @@ export function handleApplication(scope) { switch (entry.eventType) { case 'add': case 'change': - / Store / Update the file contents in memory for serving + // Store / Update the file contents in memory for serving staticFiles.set(entry.urlPath, entry.contents); break; case 'unlink': - / Remove the file from memory when it is deleted + // Remove the file from memory when it is deleted staticFiles.delete(entry.urlPath); break; } @@ -139,7 +139,7 @@ export function handleApplication(scope) { (req, next) => { if (req.method !== 'GET') return next(req); - / Attempt to retrieve the requested static file from memory + // Attempt to retrieve the requested static file from memory const staticFile = staticFiles.get(req.pathname); return staticFile @@ -213,15 +213,15 @@ For example: ```js export function handleApplication(scope) { - / Get the default EntryHandler instance + // Get the default EntryHandler instance const defaultEntryHandler = scope.handleEntry(); - / Assign a handler for the 'all' event on the default EntryHandler + // Assign a handler for the 'all' event on the default EntryHandler scope.handleEntry((entry) => { /* ... */ }); - / Create a new EntryHandler for the 'src/**/*.js' files option with a custom `'all'` event handler. + // Create a new EntryHandler for the 'src/**/*.js' files option with a custom `'all'` event handler. const customEntryHandler = scope.handleEntry( { files: 'src/**/*.js', @@ -231,7 +231,7 @@ export function handleApplication(scope) { } ); - / Create another custom EntryHandler for the 'src/**/*.ts' files option, but without a `'all'` event handler. + // Create another custom EntryHandler for the 'src/**/*.ts' files option, but without a `'all'` event handler. const anotherCustomEntryHandler = scope.handleEntry({ files: 'src/**/*.ts', }); @@ -280,7 +280,7 @@ And has the following `handleApplication(scope)` implementation: export function handleApplication(scope) { scope.options.on('change', (key, value, config) => { if (key[0] === 'files') { - / Handle the change in the files option + // Handle the change in the files option scope.logger.info(`Files option changed to: ${value}`); } }); @@ -348,9 +348,9 @@ For example, if the `files` option for `customPlugin` is changed to `web/**/*.js ```js scope.options.on('change', (key, value, config) => { - key; / ['files'] - value; / 'web/**/*.js' - config; / { files: 'web/**/*.js' } + key; // ['files'] + value; // 'web/**/*.js' + config; // { files: 'web/**/*.js' } }); ``` @@ -431,19 +431,19 @@ async function handleApplication(scope) { scope.handleEntry((entry) => { switch (entry.eventType) { case 'add': - / Handle file addition + // Handle file addition break; case 'change': - / Handle file change + // Handle file change break; case 'unlink': - / Handle file deletion + // Handle file deletion break; case 'addDir': - / Handle directory addition + // Handle directory addition break; case 'unlinkDir': - / Handle directory deletion + // Handle directory deletion break; } }); diff --git a/docs/technical-details/reference/globals.md b/docs/technical-details/reference/globals.md index 9017c161..10fe4c57 100644 --- a/docs/technical-details/reference/globals.md +++ b/docs/technical-details/reference/globals.md @@ -81,7 +81,7 @@ server.http( return request.url === '/graphql' ? handleGraphQLRequest(request) : next(request); }, { - runFirst: true, / run this handler first + runFirst: true, // run this handler first } ); ``` @@ -113,7 +113,7 @@ A `Request` object is passed to the direct static REST handlers, and preserved a ```javascript class Origin { async get(request) { - / if we are fetching data from origin, send early hints + // if we are fetching data from origin, send early hints this.getContext().requestContext.sendEarlyHints(''); let response = await fetch(request); ... diff --git a/docs/technical-details/reference/resources/index.md b/docs/technical-details/reference/resources/index.md index dcac65ca..59a5fef3 100644 --- a/docs/technical-details/reference/resources/index.md +++ b/docs/technical-details/reference/resources/index.md @@ -34,24 +34,24 @@ You can create classes that extend `Resource` to define your own data sources, t ```javascript export class MyExternalData extends Resource { - static loadAsInstance = false; / enable the updated API + static loadAsInstance = false; // enable the updated API async get(target) { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(target.id); - / do something with the response + // do something with the response } put(target, data) { - / send the data into the external source + // send the data into the external source } delete(target) { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(subscription) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -59,21 +59,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { - static loadAsInstance = false; / enable the updated API + static loadAsInstance = false; // enable the updated API get(target) { - / we can add properties or change properties before returning data: - return { ...super.get(target), newProperty: 'newValue', existingProperty: 42 }; / returns the record, with additional properties + // we can add properties or change properties before returning data: + return { ...super.get(target), newProperty: 'newValue', existingProperty: 42 }; // returns the record, with additional properties } put(target, data) { - / can change data any way we want + // can change data any way we want super.put(target, data); } delete(target) { super.delete(target); } post(target, data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -139,10 +139,10 @@ The `target` object represents the target of a request and can be used to access class extends Resource { static loadAsInstance = false; get(target) { - let param1 = target.get('param1'); / returns 'value' - let id = target.id; / returns 'some-id' - let path = target.pathname; / returns /some-id - let fullTarget = target.target; / returns /some-id?param1=value + let param1 = target.get('param1'); // returns 'value' + let id = target.id; // returns 'some-id' + let path = target.pathname; // returns /some-id + let fullTarget = target.target; // returns /some-id?param1=value ... } ``` @@ -291,11 +291,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -416,15 +416,13 @@ This will be mapped to the resource with a primary key of `test?foo=bar`, and no This will return the number of records in the table. By default, this will return an approximate count of records, which is fast and efficient. If you want an exact count, you can pass `{ exactCount: true }` as the first argument, but this will be slower and more expensive. The return value will be a Promise that resolves to an object with a `recordCount` property, which is the number of records in the table. If this was not an exact count, it will also include `estimatedRange` array with estimate range of the count. -````javascript - ### `parsePath(path, context, query) {` This is called by static methods when they are responding to a URL (from HTTP request, for example), and translates the path to an id. By default, this will parse `.property` suffixes for accessing properties and specifying preferred content type in the URL (and for older tables it will convert a multi-segment path to multipart an array id). However, in some situations you may wish to preserve the path directly as a string. You can override `parsePath` for simpler path to id preservation: ````javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ```` @@ -444,15 +442,15 @@ When using an export resource class, the REST interface will automatically creat For example, if we had a method to post a comment on a blog, and when this happens we also want to update an array of comment IDs on the blog record, but then add the comment to a separate comment table. We might do this: -````javascript +```javascript const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -570,7 +568,7 @@ let results = Product.search({ sort: { attribute: 'price' }, }); for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -640,9 +638,9 @@ If we have extended this table class with our own `get()` we can interact with t export class CustomProduct extends Product { async get(target) { let record = await super.get(target); - let name = record.name; / this is the name of the current product - let rating = record.rating; / this is the rating of the current product - / we can't directly modify the record (it is frozen), but we can copy if we want to return a modification + let name = record.name; // this is the name of the current product + let rating = record.rating; // this is the rating of the current product + // we can't directly modify the record (it is frozen), but we can copy if we want to return a modification record = { ...record, rating: 3 }; return record; } @@ -653,9 +651,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -/ if we want to update a single property: +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +// if we want to update a single property: await Product.patch(1, { rating: 3 }); ``` @@ -667,7 +665,7 @@ export class CustomProduct extends Product { let record = this.update(target); record.name = data.name; record.description = data.description; - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -700,12 +698,12 @@ export class CustomProduct extends Product { let record = this.update(target); let brandName = record.brand.name; let firstVariationPrice = record.variations[0].price; - let additionalInfoOnBrand = record.brand.additionalInfo; / not defined in schema, but can still try to access property - / make some changes - record.variations.splice(0, 1); / remove first variation - record.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = record.brand.additionalInfo; // not defined in schema, but can still try to access property + // make some changes + record.variations.splice(0, 1); // remove first variation + record.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation record.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` diff --git a/docs/technical-details/reference/resources/instance-binding.md b/docs/technical-details/reference/resources/instance-binding.md index 9017b0e0..9d1d47bc 100644 --- a/docs/technical-details/reference/resources/instance-binding.md +++ b/docs/technical-details/reference/resources/instance-binding.md @@ -12,22 +12,22 @@ This document describes the legacy instance binding behavior of the Resource cla export class MyExternalData extends Resource { static loadAsInstance = true; async get() { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(this.id); - / do something with the response + // do something with the response } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -36,21 +36,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -114,9 +114,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -143,7 +143,7 @@ The `query` argument is used to represent any additional query parameters that w ```javascript put(data, query) { - let param1 = query?.get?.('param1'); / returns 'value' + let param1 = query?.get?.('param1'); // returns 'value' ... } ``` @@ -272,11 +272,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -403,17 +403,15 @@ This will be mapped to the resource with a primary key of `test?foo=bar`, and no This will return the number of records in the table. By default, this will return an approximate count of records, which is fast and efficient. If you want an exact count, you can pass `{ exactCount: true }` as the first argument, but this will be slower and more expensive. The return value will be a Promise that resolves to an object with a `recordCount` property, which is the number of records in the table. If this was not an exact count, it will also include `estimatedRange` array with estimate range of the count. -````javascript - ### `parsePath(path, context, query) {` This is called by static methods when they are responding to a URL (from HTTP request, for example), and translates the path to an id. By default, this will parse `.property` suffixes for accessing properties and specifying preferred content type in the URL (and for older tables it will convert a multi-segment path to multipart an array id). However, in some situations you may wish to preserve the path directly as a string. You can override `parsePath` for simpler path to id preservation: -````javascript +```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } -```` +``` ### `getRecordCount: Promise<{}>` @@ -431,15 +429,15 @@ When using an export resource class, the REST interface will automatically creat For example, if we had a method to post a comment on a blog, and when this happens we also want to update an array of comment IDs on the blog record, but then add the comment to a separate comment table. We might do this: -````javascript +```javascript const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -557,7 +555,7 @@ let results = Product.search({ sort: { attribute: 'price' }, }); for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -581,10 +579,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3; / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3; // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -594,17 +592,17 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3; / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3; // modify the rating for this instance (this will be saved without a call to update()) ``` If there are additional properties on (some) products that aren't defined in the schema, we can still access them through the resource instance, but since they aren't declared, there won't be getter/setter definition for direct property access, but we can access properties with the `get(propertyName)` method and modify properties with the `set(propertyName, value)` method: ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -612,8 +610,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -626,7 +624,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -636,7 +634,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -668,12 +666,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -692,7 +690,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/docs/technical-details/reference/resources/migration.md b/docs/technical-details/reference/resources/migration.md index 1d3f091e..51ec4c83 100644 --- a/docs/technical-details/reference/resources/migration.md +++ b/docs/technical-details/reference/resources/migration.md @@ -27,15 +27,15 @@ Previous code with a `get` method: ```javascript export class MyData extends tables.MyData { async get(query) { - let id = this.getId(); / get the id + let id = this.getId(); // get the id if (query?.size > 0) { - / check number of query parameters - let idWithQuery = id + query.toString(); / add query parameters - let resource = await tables.MyData.get(idWithQuery, this); / retrieve another record - resource.newProperty = 'value'; / assign a new value to the returned resource instance + // check number of query parameters + let idWithQuery = id + query.toString(); // add query parameters + let resource = await tables.MyData.get(idWithQuery, this); // retrieve another record + resource.newProperty = 'value'; // assign a new value to the returned resource instance return resource; } else { - this.newProperty = 'value'; / assign a new value to this instance + this.newProperty = 'value'; // assign a new value to this instance return super.get(query); } } @@ -46,19 +46,19 @@ Updated code: ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior + static loadAsInstance = false; // opt in to updated behavior async get(target) { - let id = target.id; / get the id + let id = target.id; // get the id let record; if (target.size > 0) { - / check number of query parameters - let idWithQuery = target.toString(); / this is the full target with the path query parameters - / we can retrieve another record from this table directly with this.get/super.get or with tables.MyData.get + // check number of query parameters + let idWithQuery = target.toString(); // this is the full target with the path query parameters + // we can retrieve another record from this table directly with this.get/super.get or with tables.MyData.get record = await super.get(idWithQuery); } else { - record = await super.get(target); / we can just directly use the target as well + record = await super.get(target); // we can just directly use the target as well } - / the record itself is frozen, but we can copy/assign to a new object with additional properties if we want + // the record itself is frozen, but we can copy/assign to a new object with additional properties if we want return { ...record, newProperty: 'value' }; } } @@ -70,11 +70,11 @@ Previous code with a `get` method: ```javascript export class MyData extends tables.MyData { allowRead(user) { - / allow any authenticated user + // allow any authenticated user return user ? true : false; } async get(query) { - / any get logic + // any get logic return super.get(query); } } @@ -82,14 +82,14 @@ export class MyData extends tables.MyData { ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior + static loadAsInstance = false; // opt in to updated behavior async get(target) { - / While you can still use allowRead, it is not called before get is called, and it is generally encouraged - / to perform/call authorization explicitly in direct get, put, post methods rather than using allow* methods. + // While you can still use allowRead, it is not called before get is called, and it is generally encouraged + // to perform/call authorization explicitly in direct get, put, post methods rather than using allow* methods. if (!this.getContext().user) throw new Error('Unauthorized'); - target.checkPermissions = false; / authorization complete, no need to further check permissions below - / target.checkPermissions is set to true or left in place, this default get method will perform the default permissions checks - return super.get(target); / we can just directly use the query as well + target.checkPermissions = false; // authorization complete, no need to further check permissions below + // target.checkPermissions is set to true or left in place, this default get method will perform the default permissions checks + return super.get(target); // we can just directly use the query as well } } ``` @@ -102,12 +102,12 @@ export class MyData extends tables.MyData { async post(data, query) { let resource = await tables.MyData.get(data.id, this); if (resource) { - / update a property + // update a property resource.someProperty = 'value'; - / or + // or tables.MyData.patch(data.id, { someProperty: 'value' }, this); } else { - / create a new record + // create a new record MyData.create(data, this); } } @@ -118,18 +118,18 @@ Updated code: ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior - / IMPORTANT: arguments are reversed: + static loadAsInstance = false; // opt in to updated behavior + // IMPORTANT: arguments are reversed: async post(target, data) { let record = await this.get(data.id); if (record) { - / update a property - const updatable = await this.update(data.id); / we can alternately pass a target to update + // update a property + const updatable = await this.update(data.id); // we can alternately pass a target to update updatable.someProperty = 'value'; - / or + // or this.patch(data.id, { someProperty: 'value' }); } else { - / create a new record + // create a new record this.create(data); } } diff --git a/docs/technical-details/reference/transactions.md b/docs/technical-details/reference/transactions.md index 11a8f4dc..7e8546fb 100644 --- a/docs/technical-details/reference/transactions.md +++ b/docs/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started). diff --git a/site/versioned_docs/version-4.1/security/users-and-roles.md b/site/versioned_docs/version-4.1/security/users-and-roles.md index 1801fae2..77c8ec82 100644 --- a/site/versioned_docs/version-4.1/security/users-and-roles.md +++ b/site/versioned_docs/version-4.1/security/users-and-roles.md @@ -115,17 +115,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.2/developers/applications/caching.md b/site/versioned_docs/version-4.2/developers/applications/caching.md index 268adb74..f6181c30 100644 --- a/site/versioned_docs/version-4.2/developers/applications/caching.md +++ b/site/versioned_docs/version-4.2/developers/applications/caching.md @@ -82,14 +82,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -106,7 +106,7 @@ One way to provide more active caching is to specifically invalidate individual const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { - if (data.invalidate) / use this flag as a marker + if (data.invalidate) // use this flag as a marker this.invalidate(); } } @@ -122,15 +122,15 @@ We can provide more control of an active cache with subscriptions. If there is a class ThirdPartyAPI extends Resource { async *subscribe() { do { - / get the next data change event from the source + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change } while(true); } async get() { @@ -161,7 +161,7 @@ By default, HarperDB will only run the subscribe method on one thread. HarperDB ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -214,9 +214,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -244,7 +244,7 @@ class BlogSource extends Resource { get() { let post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/site/versioned_docs/version-4.2/developers/applications/index.md b/site/versioned_docs/version-4.2/developers/applications/index.md index dca036c4..11c30d12 100644 --- a/site/versioned_docs/version-4.2/developers/applications/index.md +++ b/site/versioned_docs/version-4.2/developers/applications/index.md @@ -246,12 +246,12 @@ So far we have built an application entirely through schema configuration. Howev To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the HarperDB provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the HarperDB provided set of tables (in the default database) export class DogWithHumanAge extends Dog { get(query) { - this.humanAge = 15 + this.age * 5; / silly calculation of human age equivalent + this.humanAge = 15 + this.age * 5; // silly calculation of human age equivalent return super.get(query); } } @@ -273,8 +273,8 @@ type Breed @table { And next we will use this table in our `get()` method. We will call the new table's (static) `get()` method to retrieve a breed by id. To do this correctly, we access the table using our current context by passing in `this` as the second argument. This is important because it ensures that we are accessing the data atomically, in a consistent snapshot across tables. This provides automatically tracking of most recently updated timestamps across resources for caching purposes. This allows for sharing of contextual metadata (like user who requested the data), and ensure transactional atomicity for any writes (not needed in this get operation, but important for other operations). The resource methods are automatically wrapped with a transaction (will commit/finish when the method completes), and this allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { async get(query) { let breedDescription = await Breed.get(this.breed, this); @@ -316,7 +316,7 @@ Any methods that are not defined will fall back to HarperDB's default authorizat You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` @@ -328,13 +328,13 @@ This will allow requests to url like / to be directly resolved to this resource. We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table -class BreedSource extends Resource { / define a data source +const { Breed } = tables; // our Breed table +class BreedSource extends Resource { // define a data source async get() { return (await fetch(`https://best-dog-site.com/${this.getId()}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/site/versioned_docs/version-4.2/developers/components/writing-extensions.md b/site/versioned_docs/version-4.2/developers/components/writing-extensions.md index 6a9ba7a0..34e51797 100644 --- a/site/versioned_docs/version-4.2/developers/components/writing-extensions.md +++ b/site/versioned_docs/version-4.2/developers/components/writing-extensions.md @@ -37,7 +37,7 @@ Once a user has configured an extension, HarperDB will attempt to load the exten ```javascript export function start(options: { port: number, server: {}}) { options.server.http(async (request, nextLayer) => { - / we can directly return a response here, or do some processing on the request and delegate to the next layer + // we can directly return a response here, or do some processing on the request and delegate to the next layer let response = await nextLayer(request); return response; }); @@ -49,9 +49,9 @@ Here, the `request` object will have the following structure (this is based on N ```typescript interface Request { method: string - headers: Headers / use request.headers.get(headerName) to get header values + headers: Headers // use request.headers.get(headerName) to get header values body: Stream - data: any / deserialized data from the request body + data: any // deserialized data from the request body } ``` @@ -60,8 +60,8 @@ The returned `response` object should have the following structure (again, follo ```typescript interface Response { status?: number - headers?: {} / an object with header name/values - data?: any / object/value that will be serialized into the body + headers?: {} // an object with header name/values + data?: any // object/value that will be serialized into the body body?: Stream } ``` @@ -73,15 +73,15 @@ export function start(options: { port: number, server: {}, resources: Map}) { options.server.http((request, nextLayer) => { let authorization = request.headers.authorization; if (authorization) { - / get some token for the user and determine the user - / if we want to use harperdb's user database + // get some token for the user and determine the user + // if we want to use harperdb's user database let user = server.getUser(username, password); - request.user = user; / authenticate user object goes on the request + request.user = user; // authenticate user object goes on the request } - / continue on to the next layer + // continue on to the next layer return nextLayer(request); }); - / if you needed to add a login resource, could add it as well: + // if you needed to add a login resource, could add it as well: resources.set('/login', LoginResource); } ``` @@ -112,8 +112,8 @@ And in our extension module, we could implement `handleFile`: ```javascript export function handleFile?(contents, relative_path, file_path, resources) { - / will be called for each .ejs file. - / We can then add the generate resource: + // will be called for each .ejs file. + // We can then add the generate resource: resources.set(relative_path, GeneratedResource); } ``` diff --git a/site/versioned_docs/version-4.2/developers/real-time.md b/site/versioned_docs/version-4.2/developers/real-time.md index d87f03e7..9db20eff 100644 --- a/site/versioned_docs/version-4.2/developers/real-time.md +++ b/site/versioned_docs/version-4.2/developers/real-time.md @@ -76,7 +76,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -86,8 +86,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -101,13 +101,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -121,7 +121,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/site/versioned_docs/version-4.2/developers/security/users-and-roles.md b/site/versioned_docs/version-4.2/developers/security/users-and-roles.md index 6c9fbfb5..ca44ec8a 100644 --- a/site/versioned_docs/version-4.2/developers/security/users-and-roles.md +++ b/site/versioned_docs/version-4.2/developers/security/users-and-roles.md @@ -98,17 +98,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.2/technical-details/reference/resource.md b/site/versioned_docs/version-4.2/technical-details/reference/resource.md index cb775d4c..2da897fa 100644 --- a/site/versioned_docs/version-4.2/technical-details/reference/resource.md +++ b/site/versioned_docs/version-4.2/technical-details/reference/resource.md @@ -19,21 +19,21 @@ You can create classes that extend Resource to define your own data sources, typ ```javascript export class MyExternalData extends Resource { get() { - / fetch data from an external source, using our primary key + // fetch data from an external source, using our primary key this.fetch(this.id) } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a HarperDB data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a HarperDB data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -42,21 +42,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -117,9 +117,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -254,11 +254,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -318,7 +318,7 @@ This is called by static methods when they are responding to a URL (from HTTP re ```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ``` @@ -340,10 +340,10 @@ const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -379,7 +379,7 @@ let results = Product.search({ select: ['id', 'name', 'price', 'rating'], }) for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` `AsyncIterable`s can be returned from resource methods, and will be properly serialized in responses. When a query is performed, this will open/reserve a read transaction until the query results are iterated, either through your own `for await` loop or through serialization. Failing to iterate the results this will result in a long-lived read transaction which can degrade performance (including write performance), and may eventually be aborted. @@ -402,10 +402,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3 / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3 // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -415,9 +415,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3 / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3 // modify the rating for this instance (this will be saved without a call to update()) ``` @@ -425,8 +425,8 @@ If there are additional properties on (some) products that aren't defined in the ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -434,8 +434,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -448,7 +448,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -458,7 +458,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -490,12 +490,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -514,7 +514,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/site/versioned_docs/version-4.2/technical-details/reference/transactions.md b/site/versioned_docs/version-4.2/technical-details/reference/transactions.md index 8dbb70ca..0b2e54ae 100644 --- a/site/versioned_docs/version-4.2/technical-details/reference/transactions.md +++ b/site/versioned_docs/version-4.2/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started). diff --git a/site/versioned_docs/version-4.3/developers/applications/caching.md b/site/versioned_docs/version-4.3/developers/applications/caching.md index a5bf75c3..10f70f84 100644 --- a/site/versioned_docs/version-4.3/developers/applications/caching.md +++ b/site/versioned_docs/version-4.3/developers/applications/caching.md @@ -85,14 +85,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -109,7 +109,7 @@ One way to provide more active caching is to specifically invalidate individual const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { - if (data.invalidate) / use this flag as a marker + if (data.invalidate) // use this flag as a marker this.invalidate(); } } @@ -124,16 +124,16 @@ We can provide more control of an active cache with subscriptions. If there is a ```javascript class ThirdPartyAPI extends Resource { async *subscribe() { - setInterval(() => { / every second retrieve more data - / get the next data change event from the source + setInterval(() => { // every second retrieve more data + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change }, 1000); } async get() { @@ -164,7 +164,7 @@ By default, HarperDB will only run the subscribe method on one thread. HarperDB ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -217,9 +217,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -239,7 +239,7 @@ class BlogSource extends Resource { get() { let post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/site/versioned_docs/version-4.3/developers/applications/index.md b/site/versioned_docs/version-4.3/developers/applications/index.md index ceb2aef6..51c47624 100644 --- a/site/versioned_docs/version-4.3/developers/applications/index.md +++ b/site/versioned_docs/version-4.3/developers/applications/index.md @@ -247,12 +247,12 @@ So far we have built an application entirely through schema configuration. Howev To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the HarperDB provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the HarperDB provided set of tables (in the default database) export class DogWithHumanAge extends Dog { get(query) { - this.humanAge = 15 + this.age * 5; / silly calculation of human age equivalent + this.humanAge = 15 + this.age * 5; // silly calculation of human age equivalent return super.get(query); } } @@ -274,8 +274,8 @@ type Breed @table { And next we will use this table in our `get()` method. We will call the new table's (static) `get()` method to retrieve a breed by id. To do this correctly, we access the table using our current context by passing in `this` as the second argument. This is important because it ensures that we are accessing the data atomically, in a consistent snapshot across tables. This provides automatically tracking of most recently updated timestamps across resources for caching purposes. This allows for sharing of contextual metadata (like user who requested the data), and ensure transactional atomicity for any writes (not needed in this get operation, but important for other operations). The resource methods are automatically wrapped with a transaction (will commit/finish when the method completes), and this allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { async get(query) { let breedDescription = await Breed.get(this.breed, this); @@ -317,25 +317,25 @@ Any methods that are not defined will fall back to HarperDB's default authorizat You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` -This will allow requests to url like / to be directly resolved to this resource. +This will allow requests to url like // to be directly resolved to this resource. ## Define Custom Data Sources We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table -class BreedSource extends Resource { / define a data source +const { Breed } = tables; // our Breed table +class BreedSource extends Resource { // define a data source async get() { return (await fetch(`https://best-dog-site.com/${this.getId()}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/site/versioned_docs/version-4.3/developers/components/writing-extensions.md b/site/versioned_docs/version-4.3/developers/components/writing-extensions.md index 653a1ed1..2435841d 100644 --- a/site/versioned_docs/version-4.3/developers/components/writing-extensions.md +++ b/site/versioned_docs/version-4.3/developers/components/writing-extensions.md @@ -37,7 +37,7 @@ Once a user has configured an extension, HarperDB will attempt to load the exten ```javascript export function start(options: { port: number, server: {}}) { options.server.http(async (request, nextLayer) => { - / we can directly return a response here, or do some processing on the request and delegate to the next layer + // we can directly return a response here, or do some processing on the request and delegate to the next layer let response = await nextLayer(request); return response; }); @@ -49,9 +49,9 @@ Here, the `request` object will have the following structure (this is based on N ```typescript interface Request { method: string - headers: Headers / use request.headers.get(headerName) to get header values + headers: Headers // use request.headers.get(headerName) to get header values body: Stream - data: any / deserialized data from the request body + data: any // deserialized data from the request body } ``` @@ -60,8 +60,8 @@ The returned `response` object should have the following structure (again, follo ```typescript interface Response { status?: number - headers?: {} / an object with header name/values - data?: any / object/value that will be serialized into the body + headers?: {} // an object with header name/values + data?: any // object/value that will be serialized into the body body?: Stream } ``` @@ -74,15 +74,15 @@ export function start(options: { port: number, server: {}, resources: Map}) { options.server.http((request, nextLayer) => { let authorization = request.headers.authorization; if (authorization) { - / get some token for the user and determine the user - / if we want to use harperdb's user database + // get some token for the user and determine the user + // if we want to use harperdb's user database let user = server.getUser(username, password); - request.user = user; / authenticate user object goes on the request + request.user = user; // authenticate user object goes on the request } - / continue on to the next layer + // continue on to the next layer return nextLayer(request); }, { runFirst: true }); - / if you needed to add a login resource, could add it as well: + // if you needed to add a login resource, could add it as well: resources.set('/login', LoginResource); } ``` @@ -93,7 +93,7 @@ If you were implementing a new protocol, you can directly interact with the sock ```javascript export function start(options: { port: number, server: {}}) { options.server.socket((socket) => { - / called for each incoming socket + // called for each incoming socket }); }) ``` @@ -103,7 +103,7 @@ If you were implementing a protocol using WebSockets, you can define a listener ```javascript export function start(options) { server.ws((socket) => { - / called for each incoming WebSocket + // called for each incoming WebSocket }, Object.assign({ subProtocol: 'my-cool-protocol' }, options)); }) ``` @@ -125,8 +125,8 @@ And in our extension module, we could implement `handleFile`: ```javascript export function handleFile?(contents, relative_path, file_path, resources) { - / will be called for each .ejs file. - / We can then add the generate resource: + // will be called for each .ejs file. + // We can then add the generate resource: resources.set(relative_path, GeneratedResource); } ``` diff --git a/site/versioned_docs/version-4.3/developers/real-time.md b/site/versioned_docs/version-4.3/developers/real-time.md index 8b400b78..5e9ab20a 100644 --- a/site/versioned_docs/version-4.3/developers/real-time.md +++ b/site/versioned_docs/version-4.3/developers/real-time.md @@ -77,7 +77,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -87,8 +87,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -102,13 +102,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -122,7 +122,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/site/versioned_docs/version-4.3/developers/security/users-and-roles.md b/site/versioned_docs/version-4.3/developers/security/users-and-roles.md index d490edf0..fa1bc5e7 100644 --- a/site/versioned_docs/version-4.3/developers/security/users-and-roles.md +++ b/site/versioned_docs/version-4.3/developers/security/users-and-roles.md @@ -98,17 +98,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.3/technical-details/reference/globals.md b/site/versioned_docs/version-4.3/technical-details/reference/globals.md index 8e29ddd0..86dfee72 100644 --- a/site/versioned_docs/version-4.3/technical-details/reference/globals.md +++ b/site/versioned_docs/version-4.3/technical-details/reference/globals.md @@ -81,7 +81,7 @@ server.http((request, next) => { ? handleGraphQLRequest(request) : next(request); }, { - runFirst: true, / run this handler first + runFirst: true, // run this handler first }); ``` diff --git a/site/versioned_docs/version-4.3/technical-details/reference/resource.md b/site/versioned_docs/version-4.3/technical-details/reference/resource.md index d1bc89f1..ab0ba214 100644 --- a/site/versioned_docs/version-4.3/technical-details/reference/resource.md +++ b/site/versioned_docs/version-4.3/technical-details/reference/resource.md @@ -32,22 +32,22 @@ You can create classes that extend `Resource` to define your own data sources, t ```javascript export class MyExternalData extends Resource { async get() { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(this.id); - / do something with the response + // do something with the response } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a HarperDB data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a HarperDB data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -56,21 +56,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -131,9 +131,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -160,7 +160,7 @@ The `query` argument is used to represent any additional query parameters that w ```javascript put(data, query) { - let param1 = query?.get?.('param1'); / returns 'value' + let param1 = query?.get?.('param1'); // returns 'value' ... } ``` @@ -287,11 +287,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -374,7 +374,7 @@ This is called by static methods when they are responding to a URL (from HTTP re ```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ``` @@ -396,10 +396,10 @@ const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -496,7 +496,7 @@ let results = Product.search({ sort: { attribute: 'price' } }) for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` `AsyncIterable`s can be returned from resource methods, and will be properly serialized in responses. When a query is performed, this will open/reserve a read transaction until the query results are iterated, either through your own `for await` loop or through serialization. Failing to iterate the results this will result in a long-lived read transaction which can degrade performance (including write performance), and may eventually be aborted. @@ -519,10 +519,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3 / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3 // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -532,9 +532,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3 / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3 // modify the rating for this instance (this will be saved without a call to update()) ``` @@ -542,8 +542,8 @@ If there are additional properties on (some) products that aren't defined in the ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -551,8 +551,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -565,7 +565,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -575,7 +575,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -607,12 +607,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -631,7 +631,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/site/versioned_docs/version-4.3/technical-details/reference/transactions.md b/site/versioned_docs/version-4.3/technical-details/reference/transactions.md index 8dbb70ca..0b2e54ae 100644 --- a/site/versioned_docs/version-4.3/technical-details/reference/transactions.md +++ b/site/versioned_docs/version-4.3/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started). diff --git a/site/versioned_docs/version-4.4/developers/applications/caching.md b/site/versioned_docs/version-4.4/developers/applications/caching.md index f14995e3..2ef7b1c4 100644 --- a/site/versioned_docs/version-4.4/developers/applications/caching.md +++ b/site/versioned_docs/version-4.4/developers/applications/caching.md @@ -85,14 +85,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -109,7 +109,7 @@ One way to provide more active caching is to specifically invalidate individual const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { - if (data.invalidate) / use this flag as a marker + if (data.invalidate) // use this flag as a marker this.invalidate(); } } @@ -124,16 +124,16 @@ We can provide more control of an active cache with subscriptions. If there is a ```javascript class ThirdPartyAPI extends Resource { async *subscribe() { - setInterval(() => { / every second retrieve more data - / get the next data change event from the source + setInterval(() => { // every second retrieve more data + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change }, 1000); } async get() { @@ -164,7 +164,7 @@ By default, Harper will only run the subscribe method on one thread. Harper is m ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -217,9 +217,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -239,7 +239,7 @@ class BlogSource extends Resource { get() { const post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of post.comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/site/versioned_docs/version-4.4/developers/applications/index.md b/site/versioned_docs/version-4.4/developers/applications/index.md index 4a5cecf4..4148bb7b 100644 --- a/site/versioned_docs/version-4.4/developers/applications/index.md +++ b/site/versioned_docs/version-4.4/developers/applications/index.md @@ -249,12 +249,12 @@ So far we have built an application entirely through schema configuration. Howev To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the Harper provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the Harper provided set of tables (in the default database) export class DogWithHumanAge extends Dog { get(query) { - this.humanAge = 15 + this.age * 5; / silly calculation of human age equivalent + this.humanAge = 15 + this.age * 5; // silly calculation of human age equivalent return super.get(query); } } @@ -276,8 +276,8 @@ type Breed @table { And next we will use this table in our `get()` method. We will call the new table's (static) `get()` method to retrieve a breed by id. To do this correctly, we access the table using our current context by passing in `this` as the second argument. This is important because it ensures that we are accessing the data atomically, in a consistent snapshot across tables. This provides automatically tracking of most recently updated timestamps across resources for caching purposes. This allows for sharing of contextual metadata (like user who requested the data), and ensure transactional atomicity for any writes (not needed in this get operation, but important for other operations). The resource methods are automatically wrapped with a transaction (will commit/finish when the method completes), and this allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { async get(query) { let breedDescription = await Breed.get(this.breed, this); @@ -319,7 +319,7 @@ Any methods that are not defined will fall back to Harper's default authorizatio You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` @@ -331,13 +331,13 @@ This will allow requests to url like / to be directly resolved to this resource. We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table -class BreedSource extends Resource { / define a data source +const { Breed } = tables; // our Breed table +class BreedSource extends Resource { // define a data source async get() { return (await fetch(`https://best-dog-site.com/${this.getId()}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/site/versioned_docs/version-4.4/developers/components/reference.md b/site/versioned_docs/version-4.4/developers/components/reference.md index 42fc5b0e..002258eb 100644 --- a/site/versioned_docs/version-4.4/developers/components/reference.md +++ b/site/versioned_docs/version-4.4/developers/components/reference.md @@ -153,11 +153,11 @@ graphqlSchema: In order for an extension to be classified as a Resource Extension it must implement at least one of the `handleFile()`, `handleDirectory()`, `setupFile()`, or `setupDirectory()` methods. As a standalone extension, these methods should be named and exported directly. For example: ```js -/ ESM +// ESM export function handleFile() {} export function setupDirectory() {} -/ or CJS +// or CJS function handleDirectory() {} function setupFile() {} diff --git a/site/versioned_docs/version-4.4/developers/real-time.md b/site/versioned_docs/version-4.4/developers/real-time.md index 0e73f64d..639cfce4 100644 --- a/site/versioned_docs/version-4.4/developers/real-time.md +++ b/site/versioned_docs/version-4.4/developers/real-time.md @@ -91,7 +91,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -101,8 +101,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -116,13 +116,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -136,7 +136,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/site/versioned_docs/version-4.4/developers/replication/sharding.md b/site/versioned_docs/version-4.4/developers/replication/sharding.md index 6aa1e1e4..2e80318c 100644 --- a/site/versioned_docs/version-4.4/developers/replication/sharding.md +++ b/site/versioned_docs/version-4.4/developers/replication/sharding.md @@ -57,7 +57,7 @@ Additionally, you can specify `replicateTo` and `replicatedConfirmation` paramet class MyTable extends tables.MyTable { put(record) { const context = this.getContext(); - context.replicateTo = 2; / or an array of node names + context.replicateTo = 2; // or an array of node names context.replicatedConfirmation = 1; return super.put(record); } diff --git a/site/versioned_docs/version-4.4/developers/security/users-and-roles.md b/site/versioned_docs/version-4.4/developers/security/users-and-roles.md index b1b5ffc3..c3c2beb3 100644 --- a/site/versioned_docs/version-4.4/developers/security/users-and-roles.md +++ b/site/versioned_docs/version-4.4/developers/security/users-and-roles.md @@ -98,17 +98,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.4/technical-details/reference/globals.md b/site/versioned_docs/version-4.4/technical-details/reference/globals.md index a5db50a7..4bed5c56 100644 --- a/site/versioned_docs/version-4.4/technical-details/reference/globals.md +++ b/site/versioned_docs/version-4.4/technical-details/reference/globals.md @@ -81,7 +81,7 @@ server.http((request, next) => { ? handleGraphQLRequest(request) : next(request); }, { - runFirst: true, / run this handler first + runFirst: true, // run this handler first }); ``` diff --git a/site/versioned_docs/version-4.4/technical-details/reference/resource.md b/site/versioned_docs/version-4.4/technical-details/reference/resource.md index bc0e8345..6d51f361 100644 --- a/site/versioned_docs/version-4.4/technical-details/reference/resource.md +++ b/site/versioned_docs/version-4.4/technical-details/reference/resource.md @@ -34,22 +34,22 @@ You can create classes that extend `Resource` to define your own data sources, t ```javascript export class MyExternalData extends Resource { async get() { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(this.id); - / do something with the response + // do something with the response } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -58,21 +58,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -136,9 +136,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -170,7 +170,7 @@ The `query` argument is used to represent any additional query parameters that w ```javascript put(data, query) { - let param1 = query?.get?.('param1'); / returns 'value' + let param1 = query?.get?.('param1'); // returns 'value' ... } ``` @@ -299,11 +299,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -416,7 +416,7 @@ This is called by static methods when they are responding to a URL (from HTTP re ```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ``` @@ -439,10 +439,10 @@ const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -557,7 +557,7 @@ let results = Product.search({ sort: { attribute: 'price' } }) for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -581,10 +581,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3 / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3 // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -594,9 +594,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3 / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3 // modify the rating for this instance (this will be saved without a call to update()) ``` @@ -604,8 +604,8 @@ If there are additional properties on (some) products that aren't defined in the ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -613,8 +613,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -627,7 +627,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -637,7 +637,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -669,12 +669,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -693,7 +693,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/site/versioned_docs/version-4.4/technical-details/reference/transactions.md b/site/versioned_docs/version-4.4/technical-details/reference/transactions.md index 8c712122..e0851655 100644 --- a/site/versioned_docs/version-4.4/technical-details/reference/transactions.md +++ b/site/versioned_docs/version-4.4/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started). diff --git a/site/versioned_docs/version-4.5/developers/applications/caching.md b/site/versioned_docs/version-4.5/developers/applications/caching.md index 63188e85..ddd3c8ec 100644 --- a/site/versioned_docs/version-4.5/developers/applications/caching.md +++ b/site/versioned_docs/version-4.5/developers/applications/caching.md @@ -85,14 +85,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -109,7 +109,7 @@ One way to provide more active caching is to specifically invalidate individual const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { - if (data.invalidate) / use this flag as a marker + if (data.invalidate) // use this flag as a marker this.invalidate(); } } @@ -124,16 +124,16 @@ We can provide more control of an active cache with subscriptions. If there is a ```javascript class ThirdPartyAPI extends Resource { async *subscribe() { - setInterval(() => { / every second retrieve more data - / get the next data change event from the source + setInterval(() => { // every second retrieve more data + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change }, 1000); } async get() { @@ -164,7 +164,7 @@ By default, Harper will only run the subscribe method on one thread. Harper is m ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -217,9 +217,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -239,7 +239,7 @@ class BlogSource extends Resource { get() { const post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of post.comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/site/versioned_docs/version-4.5/developers/applications/index.md b/site/versioned_docs/version-4.5/developers/applications/index.md index edf58ff4..01a84fc0 100644 --- a/site/versioned_docs/version-4.5/developers/applications/index.md +++ b/site/versioned_docs/version-4.5/developers/applications/index.md @@ -39,12 +39,12 @@ flowchart LR To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the Harper provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the Harper provided set of tables (in the default database) export class DogWithHumanAge extends Dog { get(query) { - this.humanAge = 15 + this.age * 5; / silly calculation of human age equivalent + this.humanAge = 15 + this.age * 5; // silly calculation of human age equivalent return super.get(query); } } @@ -66,8 +66,8 @@ type Breed @table { And next we will use this table in our `get()` method. We will call the new table's (static) `get()` method to retrieve a breed by id. To do this correctly, we access the table using our current context by passing in `this` as the second argument. This is important because it ensures that we are accessing the data atomically, in a consistent snapshot across tables. This provides automatically tracking of most recently updated timestamps across resources for caching purposes. This allows for sharing of contextual metadata (like user who requested the data), and ensure transactional atomicity for any writes (not needed in this get operation, but important for other operations). The resource methods are automatically wrapped with a transaction (will commit/finish when the method completes), and this allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { async get(query) { let breedDescription = await Breed.get(this.breed, this); @@ -109,25 +109,25 @@ Any methods that are not defined will fall back to Harper's default authorizatio You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` -This will allow requests to url like / to be directly resolved to this resource. +This will allow requests to url like // to be directly resolved to this resource. ## Define Custom Data Sources We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table -class BreedSource extends Resource { / define a data source +const { Breed } = tables; // our Breed table +class BreedSource extends Resource { // define a data source async get() { return (await fetch(`https://best-dog-site.com/${this.getId()}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/site/versioned_docs/version-4.5/developers/components/reference.md b/site/versioned_docs/version-4.5/developers/components/reference.md index 42fc5b0e..002258eb 100644 --- a/site/versioned_docs/version-4.5/developers/components/reference.md +++ b/site/versioned_docs/version-4.5/developers/components/reference.md @@ -153,11 +153,11 @@ graphqlSchema: In order for an extension to be classified as a Resource Extension it must implement at least one of the `handleFile()`, `handleDirectory()`, `setupFile()`, or `setupDirectory()` methods. As a standalone extension, these methods should be named and exported directly. For example: ```js -/ ESM +// ESM export function handleFile() {} export function setupDirectory() {} -/ or CJS +// or CJS function handleDirectory() {} function setupFile() {} diff --git a/site/versioned_docs/version-4.5/developers/real-time.md b/site/versioned_docs/version-4.5/developers/real-time.md index 0e73f64d..639cfce4 100644 --- a/site/versioned_docs/version-4.5/developers/real-time.md +++ b/site/versioned_docs/version-4.5/developers/real-time.md @@ -91,7 +91,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -101,8 +101,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -116,13 +116,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -136,7 +136,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/site/versioned_docs/version-4.5/developers/replication/sharding.md b/site/versioned_docs/version-4.5/developers/replication/sharding.md index c9d747ff..869d353b 100644 --- a/site/versioned_docs/version-4.5/developers/replication/sharding.md +++ b/site/versioned_docs/version-4.5/developers/replication/sharding.md @@ -61,7 +61,7 @@ Additionally, you can specify `replicateTo` and `replicatedConfirmation` paramet class MyTable extends tables.MyTable { put(record) { const context = this.getContext(); - context.replicateTo = 2; / or an array of node names + context.replicateTo = 2; // or an array of node names context.replicatedConfirmation = 1; return super.put(record); } @@ -108,13 +108,13 @@ Alternately you can define a custom sharding strategy based on the primary key a ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? 1 : 2; / return shard number + return id % 2 === 0 ? 1 : 2; // return shard number }); ``` or ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? ['node1'] : ['node2']; / return array of node hostnames + return id % 2 === 0 ? ['node1'] : ['node2']; // return array of node hostnames }); ``` diff --git a/site/versioned_docs/version-4.5/developers/security/users-and-roles.md b/site/versioned_docs/version-4.5/developers/security/users-and-roles.md index b1b5ffc3..c3c2beb3 100644 --- a/site/versioned_docs/version-4.5/developers/security/users-and-roles.md +++ b/site/versioned_docs/version-4.5/developers/security/users-and-roles.md @@ -98,17 +98,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.5/technical-details/reference/blob.md b/site/versioned_docs/version-4.5/technical-details/reference/blob.md index e3b55918..eb1ff72e 100644 --- a/site/versioned_docs/version-4.5/technical-details/reference/blob.md +++ b/site/versioned_docs/version-4.5/technical-details/reference/blob.md @@ -32,7 +32,7 @@ export class MyEndpoint extends MyTable { return { status: 200, headers: {}, - body: this.data, / this.data is a blob + body: this.data, // this.data is a blob }); } } @@ -41,11 +41,11 @@ One of the important characteristics of blobs is they natively support asynchron ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is still being written to storage +// at this point the blob exists, but the data is still being written to storage await MyTable.put({ id: 'my-record', data: blob }); -/ we now have written a record that references the blob +// we now have written a record that references the blob let record = await MyTable.get('my-record'); -/ we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. +// we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. let stream = record.data.stream(); ``` This can be powerful functionality for large media content, where content can be streamed into storage as it streamed out in real-time to users as it is received. @@ -53,9 +53,9 @@ Alternately, we can also wait for the blob to be fully written to storage before ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is was not been written to storage +// at this point the blob exists, but the data is was not been written to storage await blob.save(MyTable); -/ we now know the blob is fully written to storage +// we now know the blob is fully written to storage await MyTable.put({ id: 'my-record', data: blob }); ``` @@ -68,7 +68,7 @@ Because blobs can be streamed and referenced prior to their completion, there is export class MyEndpoint extends MyTable { let blob = this.data; blob.on('error', () => { - / if this was a caching table, we may want to invalidate or delete this record: + // if this was a caching table, we may want to invalidate or delete this record: this.invalidate(); }); async get() { @@ -87,11 +87,11 @@ Blobs that are created from streams may not have the standard `size` property av ```javascript let record = await MyTable.get('my-record'); let blob = record.data; -blob.size / will be available if it was saved with a known size -let stream blob.stream(); / start streaming the data +blob.size // will be available if it was saved with a known size +let stream blob.stream(); // start streaming the data if (blob.size === undefined) { blob.on('size', (size) => { - / will be called once the size is available + // will be called once the size is available }) } diff --git a/site/versioned_docs/version-4.5/technical-details/reference/globals.md b/site/versioned_docs/version-4.5/technical-details/reference/globals.md index ac618756..673334d9 100644 --- a/site/versioned_docs/version-4.5/technical-details/reference/globals.md +++ b/site/versioned_docs/version-4.5/technical-details/reference/globals.md @@ -81,7 +81,7 @@ server.http((request, next) => { ? handleGraphQLRequest(request) : next(request); }, { - runFirst: true, / run this handler first + runFirst: true, // run this handler first }); ``` @@ -111,7 +111,7 @@ A `Request` object is passed to the direct static REST handlers, and preserved a ```javascript class Origin { async get(request) { - / if we are fetching data from origin, send early hints + // if we are fetching data from origin, send early hints this.getContext().requestContext.sendEarlyHints(''); let response = await fetch(request); ... diff --git a/site/versioned_docs/version-4.5/technical-details/reference/resource.md b/site/versioned_docs/version-4.5/technical-details/reference/resource.md index b69a2e49..17bba1d3 100644 --- a/site/versioned_docs/version-4.5/technical-details/reference/resource.md +++ b/site/versioned_docs/version-4.5/technical-details/reference/resource.md @@ -34,22 +34,22 @@ You can create classes that extend `Resource` to define your own data sources, t ```javascript export class MyExternalData extends Resource { async get() { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(this.id); - / do something with the response + // do something with the response } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -58,21 +58,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -136,9 +136,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -165,7 +165,7 @@ The `query` argument is used to represent any additional query parameters that w ```javascript put(data, query) { - let param1 = query?.get?.('param1'); / returns 'value' + let param1 = query?.get?.('param1'); // returns 'value' ... } ``` @@ -294,11 +294,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -420,15 +420,13 @@ This will be mapped to the resource with a primary key of `test?foo=bar`, and no ### `getRecordCount({ exactCount: boolean })` This will return the number of records in the table. By default, this will return an approximate count of records, which is fast and efficient. If you want an exact count, you can pass `{ exactCount: true }` as the first argument, but this will be slower and more expensive. The return value will be a Promise that resolves to an object with a `recordCount` property, which is the number of records in the table. If this was not an exact count, it will also include `estimatedRange` array with estimate range of the count. -```javascript - ### `parsePath(path, context, query) {` This is called by static methods when they are responding to a URL (from HTTP request, for example), and translates the path to an id. By default, this will parse `.property` suffixes for accessing properties and specifying preferred content type in the URL (and for older tables it will convert a multi-segment path to multipart an array id). However, in some situations you may wish to preserve the path directly as a string. You can override `parsePath` for simpler path to id preservation: ```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ``` @@ -453,10 +451,10 @@ const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -571,7 +569,7 @@ let results = Product.search({ sort: `{ attribute: 'price' }` }) for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -595,10 +593,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3 / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3 // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -608,9 +606,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3 / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3 // modify the rating for this instance (this will be saved without a call to update()) ``` @@ -618,8 +616,8 @@ If there are additional properties on (some) products that aren't defined in the ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -627,8 +625,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -641,7 +639,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -651,7 +649,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -683,12 +681,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -707,7 +705,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/site/versioned_docs/version-4.5/technical-details/reference/transactions.md b/site/versioned_docs/version-4.5/technical-details/reference/transactions.md index 8c712122..e0851655 100644 --- a/site/versioned_docs/version-4.5/technical-details/reference/transactions.md +++ b/site/versioned_docs/version-4.5/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started). diff --git a/site/versioned_docs/version-4.6/developers/applications/caching.md b/site/versioned_docs/version-4.6/developers/applications/caching.md index 2d97ba25..b1c57fc5 100644 --- a/site/versioned_docs/version-4.6/developers/applications/caching.md +++ b/site/versioned_docs/version-4.6/developers/applications/caching.md @@ -86,14 +86,14 @@ class ThirdPartyAPI extends Resource { async get() { const context = this.getContext(); let headers = new Headers(); - if (context.replacingVersion) / this is the existing cached record + if (context.replacingVersion) // this is the existing cached record headers.set('If-Modified-Since', new Date(context.replacingVersion).toUTCString()); let response = await fetch(`https://some-api.com/${this.getId()}`, { headers }); let cacheInfo = response.headers.get('Cache-Control'); let maxAge = cacheInfo?.match(/max-age=(\d)/)?.[1]; - if (maxAge) / we can set a specific expiration time by setting context.expiresAt - context.expiresAt = Date.now() + maxAge * 1000; / convert from seconds to milliseconds and add to current time - / we can just revalidate and return the record if the origin has confirmed that it has the same version: + if (maxAge) // we can set a specific expiration time by setting context.expiresAt + context.expiresAt = Date.now() + maxAge * 1000; // convert from seconds to milliseconds and add to current time + // we can just revalidate and return the record if the origin has confirmed that it has the same version: if (response.status === 304) return context.replacingRecord; ... ``` @@ -111,7 +111,7 @@ const { MyTable } = tables; export class MyTableEndpoint extends MyTable { async post(data) { if (data.invalidate) - / use this flag as a marker + // use this flag as a marker this.invalidate(); } } @@ -126,16 +126,16 @@ We can provide more control of an active cache with subscriptions. If there is a ```javascript class ThirdPartyAPI extends Resource { async *subscribe() { - setInterval(() => { / every second retrieve more data - / get the next data change event from the source + setInterval(() => { // every second retrieve more data + // get the next data change event from the source let update = (await fetch(`https://some-api.com/latest-update`)).json(); - const event = { / define the change event (which will update the cache) - type: 'put', / this would indicate that the event includes the new data value - id: / the primary key of the record that updated - value: / the new value of the record that updated - timestamp: / the timestamp of when the data change occurred + const event = { // define the change event (which will update the cache) + type: 'put', // this would indicate that the event includes the new data value + id: // the primary key of the record that updated + value: // the new value of the record that updated + timestamp: // the timestamp of when the data change occurred }; - yield event; / this returns this event, notifying the cache of the change + yield event; // this returns this event, notifying the cache of the change }, 1000); } async get() { @@ -166,7 +166,7 @@ By default, Harper will only run the subscribe method on one thread. Harper is m ```javascript class ThirdPartyAPI extends Resource { static subscribeOnThisThread(threadIndex) { - return threadIndex < 2; / run on two threads (the first two threads) + return threadIndex < 2; // run on two threads (the first two threads) } async *subscribe() { .... @@ -219,9 +219,9 @@ When you are using a caching table, it is important to remember that any resourc ```javascript class MyCache extends tables.MyCache { async post(data) { - / if the data is not cached locally, retrieves from source: + // if the data is not cached locally, retrieves from source: await this.ensuredLoaded(); - / now we can be sure that the data is loaded, and can access properties + // now we can be sure that the data is loaded, and can access properties this.quantity = this.quantity - data.purchases; } } @@ -241,7 +241,7 @@ class BlogSource extends Resource { get() { const post = await (await fetch(`https://my-blog-server/${this.getId()}`).json()); for (let comment of post.comments) { - await Comment.put(comment, this); / save this comment as part of our current context and transaction + await Comment.put(comment, this); // save this comment as part of our current context and transaction } return post; } diff --git a/site/versioned_docs/version-4.6/developers/applications/defining-schemas.md b/site/versioned_docs/version-4.6/developers/applications/defining-schemas.md index 85d3b612..df239ca5 100644 --- a/site/versioned_docs/version-4.6/developers/applications/defining-schemas.md +++ b/site/versioned_docs/version-4.6/developers/applications/defining-schemas.md @@ -195,7 +195,7 @@ HNSW indexing finds the nearest neighbors to a search vector. To use this, you c ```javascript let results = Product.search({ sort: { attribute: 'textEmbeddings', target: searchVector }, - limit: 5, / get the five nearest neighbors + limit: 5, // get the five nearest neighbors }); ``` @@ -205,7 +205,7 @@ This can be used in combination with other conditions as well, for example: let results = Product.search({ conditions: [{ attribute: 'price', comparator: 'lt', value: 50 }], sort: { attribute: 'textEmbeddings', target: searchVector }, - limit: 5, / get the five nearest neighbors + limit: 5, // get the five nearest neighbors }); ``` diff --git a/site/versioned_docs/version-4.6/developers/applications/index.md b/site/versioned_docs/version-4.6/developers/applications/index.md index 94a86e92..c82072a0 100644 --- a/site/versioned_docs/version-4.6/developers/applications/index.md +++ b/site/versioned_docs/version-4.6/developers/applications/index.md @@ -86,16 +86,16 @@ This guide is going to walk you through building a basic Harper application usin To define custom (JavaScript) resources as endpoints, we need to create a `resources.js` module (this goes in the root of your application folder). And then endpoints can be defined with Resource classes that `export`ed. This can be done in addition to, or in lieu of the `@export`ed types in the schema.graphql. If you are exporting and extending a table you defined in the schema make sure you remove the `@export` from the schema so that don't export the original table or resource to the same endpoint/path you are exporting with a class. Resource classes have methods that correspond to standard HTTP/REST methods, like `get`, `post`, `patch`, and `put` to implement specific handling for any of these methods (for tables they all have default implementations). To do this, we get the `Dog` class from the defined tables, extend it, and export it: ```javascript -/ resources.js: -const { Dog } = tables; / get the Dog table from the Harper provided set of tables (in the default database) +// resources.js: +const { Dog } = tables; // get the Dog table from the Harper provided set of tables (in the default database) export class DogWithHumanAge extends Dog { static loadAsInstance = false; async get(target) { const record = await super.get(target); return { - ...record, / include all properties from the record - humanAge: 15 + record.age * 5, / silly calculation of human age equivalent + ...record, // include all properties from the record + humanAge: 15 + record.age * 5, // silly calculation of human age equivalent }; } } @@ -123,14 +123,14 @@ We use the new table's (static) `get()` method to retrieve a breed by id. Harper The resource methods are automatically wrapped with a transaction and will automatically commit the changes when the method finishes. This allows us to fully utilize multiple resources in our current transaction. With our own snapshot of the database for the Dog and Breed table we can then access data like this: ```javascript -/resource.js: -const { Dog, Breed } = tables; / get the Breed table too +//resource.js: +const { Dog, Breed } = tables; // get the Breed table too export class DogWithBreed extends Dog { static loadAsInstance = false; async get(target) { - / get the Dog record + // get the Dog record const record = await super.get(target); - / get the Breed record + // get the Breed record let breedDescription = await Breed.get(record.breed); return { ...record, @@ -168,10 +168,10 @@ export class CustomDog extends Dog { async post(target, data) { if (data.action === 'add-trick') { const context = this.getContext(); - / if we want to skip the default permission checks, we can turn off checkPermissions: + // if we want to skip the default permission checks, we can turn off checkPermissions: target.checkPermissions = false; const record = this.update(target); - / and do our own/custom permission check: + // and do our own/custom permission check: if (record.owner !== context.user?.username) { throw new Error('Can not update this record'); } @@ -186,7 +186,7 @@ Any methods that are not defined will fall back to Harper's default authorizatio You can also use the `default` export to define the root path resource handler. For example: ```javascript -/ resources.json +// resources.json export default class CustomDog extends Dog { ... ``` @@ -198,14 +198,14 @@ This will allow requests to url like / to be directly resolved to this resource. We can also directly implement the Resource class and use it to create new data sources from scratch that can be used as endpoints. Custom resources can also be used as caching sources. Let's say that we defined a `Breed` table that was a cache of information about breeds from another source. We could implement a caching table like: ```javascript -const { Breed } = tables; / our Breed table +const { Breed } = tables; // our Breed table class BreedSource extends Resource { - / define a data source + // define a data source async get(target) { return (await fetch(`https://best-dog-site.com/${target}`)).json(); } } -/ define that our breed table is a cache of data from the data source above, with a specified expiration +// define that our breed table is a cache of data from the data source above, with a specified expiration Breed.sourcedFrom(BreedSource, { expiration: 3600 }); ``` diff --git a/site/versioned_docs/version-4.6/developers/real-time.md b/site/versioned_docs/version-4.6/developers/real-time.md index 1c1ed269..9c5c79e4 100644 --- a/site/versioned_docs/version-4.6/developers/real-time.md +++ b/site/versioned_docs/version-4.6/developers/real-time.md @@ -96,7 +96,7 @@ WebSockets are supported through the REST interface and go through the `connect( ```javascript let ws = new WebSocket('wss://server/my-resource/341'); ws.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` @@ -106,8 +106,8 @@ By default, the resources will make a subscription to that resource, monitoring ```javascript export class Echo extends Resource { async *connect(incomingMessages) { - for await (let message of incomingMessages) { / wait for each incoming message from the client - / and send the message back to the client + for await (let message of incomingMessages) { // wait for each incoming message from the client + // and send the message back to the client yield message; } } @@ -121,13 +121,13 @@ export class Example extends Resource { let outgoingMessages = super.connect(); let timer = setInterval(() => { outgoingMessages.send({greeting: 'hi again!'}); - }, 1000); / send a message once a second + }, 1000); // send a message once a second incomingMessages.on('data', (message) => { - / another way of echo-ing the data back to the client + // another way of echo-ing the data back to the client outgoingMessages.send(message); }); outgoingMessages.on('close', () => { - / make sure we end the timer once the connection is closed + // make sure we end the timer once the connection is closed clearInterval(timer); }); return outgoingMessages; @@ -141,7 +141,7 @@ Server Sent Events (SSE) are also supported through the REST server interface, a ```javascript let eventSource = new EventSource('https://server/my-resource/341', { withCredentials: true }); eventSource.onmessage = (event) => { - / received a notification from the server + // received a notification from the server let data = JSON.parse(event.data); }; ``` diff --git a/site/versioned_docs/version-4.6/developers/replication/sharding.md b/site/versioned_docs/version-4.6/developers/replication/sharding.md index 84197445..74242292 100644 --- a/site/versioned_docs/version-4.6/developers/replication/sharding.md +++ b/site/versioned_docs/version-4.6/developers/replication/sharding.md @@ -75,7 +75,7 @@ Additionally, you can specify `replicateTo` and `replicatedConfirmation` paramet class MyTable extends tables.MyTable { put(record) { const context = this.getContext(); - context.replicateTo = 2; / or an array of node names + context.replicateTo = 2; // or an array of node names context.replicatedConfirmation = 1; return super.put(record); } @@ -132,7 +132,7 @@ Alternately you can define a custom sharding strategy based on the primary key a ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? 1 : 2; / return shard number + return id % 2 === 0 ? 1 : 2; // return shard number }); ``` @@ -140,7 +140,7 @@ or ```javascript MyTable.setResidencyById((id) => { - return id % 2 === 0 ? ['node1'] : ['node2']; / return array of node hostnames + return id % 2 === 0 ? ['node1'] : ['node2']; // return array of node hostnames }); ``` diff --git a/site/versioned_docs/version-4.6/developers/security/users-and-roles.md b/site/versioned_docs/version-4.6/developers/security/users-and-roles.md index 80835953..76ed6901 100644 --- a/site/versioned_docs/version-4.6/developers/security/users-and-roles.md +++ b/site/versioned_docs/version-4.6/developers/security/users-and-roles.md @@ -100,17 +100,17 @@ Each table that a role should be given some level of CRUD permissions to must be ```json { - "table_name": { / the name of the table to define CRUD perms for - "read": boolean, / access to read from this table - "insert": boolean, / access to insert data to table - "update": boolean, / access to update data in table - "delete": boolean, / access to delete row data in table - "attribute_permissions": [ / permissions for specific table attributes + "table_name": { // the name of the table to define CRUD perms for + "read": boolean, // access to read from this table + "insert": boolean, // access to insert data to table + "update": boolean, // access to update data in table + "delete": boolean, // access to delete row data in table + "attribute_permissions": [ // permissions for specific table attributes { - "attribute_name": "attribute_name", / attribute to assign permissions to - "read": boolean, / access to read this attribute from table - "insert": boolean, / access to insert this attribute into the table - "update": boolean / access to update this attribute in the table + "attribute_name": "attribute_name", // attribute to assign permissions to + "read": boolean, // access to read this attribute from table + "insert": boolean, // access to insert this attribute into the table + "update": boolean // access to update this attribute in the table } ] } diff --git a/site/versioned_docs/version-4.6/technical-details/reference/blob.md b/site/versioned_docs/version-4.6/technical-details/reference/blob.md index 9b8871bd..a62ba2eb 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/blob.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/blob.md @@ -34,7 +34,7 @@ export class MyEndpoint extends MyTable { return { status: 200, headers: {}, - body: this.data, / this.data is a blob + body: this.data, // this.data is a blob }); } } @@ -44,11 +44,11 @@ One of the important characteristics of blobs is they natively support asynchron ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is still being written to storage +// at this point the blob exists, but the data is still being written to storage await MyTable.put({ id: 'my-record', data: blob }); -/ we now have written a record that references the blob +// we now have written a record that references the blob let record = await MyTable.get('my-record'); -/ we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. +// we now have a record that gives us access to the blob. We can asynchronously access the blob's data or stream the data, and it will be available as blob the stream is written to the blob. let stream = record.data.stream(); ``` @@ -57,9 +57,9 @@ Alternately, we can also wait for the blob to be fully written to storage before ```javascript let blob = await createBlob(stream); -/ at this point the blob exists, but the data is was not been written to storage +// at this point the blob exists, but the data is was not been written to storage await blob.save(MyTable); -/ we now know the blob is fully written to storage +// we now know the blob is fully written to storage await MyTable.put({ id: 'my-record', data: blob }); ``` @@ -73,7 +73,7 @@ Because blobs can be streamed and referenced prior to their completion, there is export class MyEndpoint extends MyTable { let blob = this.data; blob.on('error', () => { - / if this was a caching table, we may want to invalidate or delete this record: + // if this was a caching table, we may want to invalidate or delete this record: this.invalidate(); }); async get() { @@ -93,11 +93,11 @@ Blobs that are created from streams may not have the standard `size` property av ```javascript let record = await MyTable.get('my-record'); let blob = record.data; -blob.size / will be available if it was saved with a known size -let stream blob.stream(); / start streaming the data +blob.size // will be available if it was saved with a known size +let stream blob.stream(); // start streaming the data if (blob.size === undefined) { blob.on('size', (size) => { - / will be called once the size is available + // will be called once the size is available }) } diff --git a/site/versioned_docs/version-4.6/technical-details/reference/components/extensions.md b/site/versioned_docs/version-4.6/technical-details/reference/components/extensions.md index f72dfdf7..e5575f8e 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/components/extensions.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/components/extensions.md @@ -86,11 +86,11 @@ test-component: In order for an extension to be classified as a Resource Extension it must implement at least one of the `handleFile()`, `handleDirectory()`, `setupFile()`, or `setupDirectory()` methods. As a standalone extension, these methods should be named and exported directly. For example: ```js -/ ESM +// ESM export function handleFile() {} export function setupDirectory() {} -/ or CJS +// or CJS function handleDirectory() {} function setupFile() {} diff --git a/site/versioned_docs/version-4.6/technical-details/reference/components/plugins.md b/site/versioned_docs/version-4.6/technical-details/reference/components/plugins.md index bdc2010a..b455ad66 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/components/plugins.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/components/plugins.md @@ -84,7 +84,7 @@ The plugin module can export a `defaultTimeout` variable (in milliseconds) that For example: ```typescript -export const defaultTimeout = 60_000; / 60 seconds +export const defaultTimeout = 60_000; // 60 seconds ``` Additionally, users can specify a `timeout` option in their application's `config.yaml` file for a specific plugin. This option takes precedence over the plugin's `defaultTimeout` and the system default. @@ -110,7 +110,7 @@ export function handleApplication(scope) { scope.options.on('change', (key, value, config) => { if (key[0] === 'files' || key[0] === 'urlPath') { - / If the files or urlPath options change, we need to reinitialize the static files map + // If the files or urlPath options change, we need to reinitialize the static files map staticFiles.clear(); logger.info(`Static files reinitialized due to change in ${key.join('.')}`); } @@ -125,11 +125,11 @@ export function handleApplication(scope) { switch (entry.eventType) { case 'add': case 'change': - / Store / Update the file contents in memory for serving + // Store // Update the file contents in memory for serving staticFiles.set(entry.urlPath, entry.contents); break; case 'unlink': - / Remove the file from memory when it is deleted + // Remove the file from memory when it is deleted staticFiles.delete(entry.urlPath); break; } @@ -139,7 +139,7 @@ export function handleApplication(scope) { (req, next) => { if (req.method !== 'GET') return next(req); - / Attempt to retrieve the requested static file from memory + // Attempt to retrieve the requested static file from memory const staticFile = staticFiles.get(req.pathname); return staticFile @@ -213,15 +213,15 @@ For example: ```js export function handleApplication(scope) { - / Get the default EntryHandler instance + // Get the default EntryHandler instance const defaultEntryHandler = scope.handleEntry(); - / Assign a handler for the 'all' event on the default EntryHandler + // Assign a handler for the 'all' event on the default EntryHandler scope.handleEntry((entry) => { /* ... */ }); - / Create a new EntryHandler for the 'src/**/*.js' files option with a custom `'all'` event handler. + // Create a new EntryHandler for the 'src/**/*.js' files option with a custom `'all'` event handler. const customEntryHandler = scope.handleEntry( { files: 'src/**/*.js', @@ -231,7 +231,7 @@ export function handleApplication(scope) { } ); - / Create another custom EntryHandler for the 'src/**/*.ts' files option, but without a `'all'` event handler. + // Create another custom EntryHandler for the 'src/**/*.ts' files option, but without a `'all'` event handler. const anotherCustomEntryHandler = scope.handleEntry({ files: 'src/**/*.ts', }); @@ -280,7 +280,7 @@ And has the following `handleApplication(scope)` implementation: export function handleApplication(scope) { scope.options.on('change', (key, value, config) => { if (key[0] === 'files') { - / Handle the change in the files option + // Handle the change in the files option scope.logger.info(`Files option changed to: ${value}`); } }); @@ -348,9 +348,9 @@ For example, if the `files` option for `customPlugin` is changed to `web/**/*.js ```js scope.options.on('change', (key, value, config) => { - key; / ['files'] - value; / 'web/**/*.js' - config; / { files: 'web/**/*.js' } + key; // ['files'] + value; // 'web/**/*.js' + config; // { files: 'web/**/*.js' } }); ``` @@ -431,19 +431,19 @@ async function handleApplication(scope) { scope.handleEntry((entry) => { switch (entry.eventType) { case 'add': - / Handle file addition + // Handle file addition break; case 'change': - / Handle file change + // Handle file change break; case 'unlink': - / Handle file deletion + // Handle file deletion break; case 'addDir': - / Handle directory addition + // Handle directory addition break; case 'unlinkDir': - / Handle directory deletion + // Handle directory deletion break; } }); diff --git a/site/versioned_docs/version-4.6/technical-details/reference/globals.md b/site/versioned_docs/version-4.6/technical-details/reference/globals.md index 8a24c335..a23f2a99 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/globals.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/globals.md @@ -81,7 +81,7 @@ server.http( return request.url === '/graphql' ? handleGraphQLRequest(request) : next(request); }, { - runFirst: true, / run this handler first + runFirst: true, // run this handler first } ); ``` @@ -113,7 +113,7 @@ A `Request` object is passed to the direct static REST handlers, and preserved a ```javascript class Origin { async get(request) { - / if we are fetching data from origin, send early hints + // if we are fetching data from origin, send early hints this.getContext().requestContext.sendEarlyHints(''); let response = await fetch(request); ... diff --git a/site/versioned_docs/version-4.6/technical-details/reference/resources/index.md b/site/versioned_docs/version-4.6/technical-details/reference/resources/index.md index f1862103..236bd83f 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/resources/index.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/resources/index.md @@ -34,24 +34,24 @@ You can create classes that extend `Resource` to define your own data sources, t ```javascript export class MyExternalData extends Resource { - static loadAsInstance = false; / enable the updated API + static loadAsInstance = false; // enable the updated API async get(target) { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(target.id); - / do something with the response + // do something with the response } put(target, data) { - / send the data into the external source + // send the data into the external source } delete(target) { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(subscription) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -59,21 +59,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { - static loadAsInstance = false; / enable the updated API + static loadAsInstance = false; // enable the updated API get(target) { - / we can add properties or change properties before returning data: - return { ...super.get(target), newProperty: 'newValue', existingProperty: 42 }; / returns the record, with additional properties + // we can add properties or change properties before returning data: + return { ...super.get(target), newProperty: 'newValue', existingProperty: 42 }; // returns the record, with additional properties } put(target, data) { - / can change data any way we want + // can change data any way we want super.put(target, data); } delete(target) { super.delete(target); } post(target, data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -139,10 +139,10 @@ The `target` object represents the target of a request and can be used to access class extends Resource { static loadAsInstance = false; get(target) { - let param1 = target.get('param1'); / returns 'value' - let id = target.id; / returns 'some-id' - let path = target.pathname; / returns /some-id - let fullTarget = target.target; / returns /some-id?param1=value + let param1 = target.get('param1'); // returns 'value' + let id = target.id; // returns 'some-id' + let path = target.pathname; // returns /some-id + let fullTarget = target.target; // returns /some-id?param1=value ... } ``` @@ -291,11 +291,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -416,15 +416,13 @@ This will be mapped to the resource with a primary key of `test?foo=bar`, and no This will return the number of records in the table. By default, this will return an approximate count of records, which is fast and efficient. If you want an exact count, you can pass `{ exactCount: true }` as the first argument, but this will be slower and more expensive. The return value will be a Promise that resolves to an object with a `recordCount` property, which is the number of records in the table. If this was not an exact count, it will also include `estimatedRange` array with estimate range of the count. -````javascript - ### `parsePath(path, context, query) {` This is called by static methods when they are responding to a URL (from HTTP request, for example), and translates the path to an id. By default, this will parse `.property` suffixes for accessing properties and specifying preferred content type in the URL (and for older tables it will convert a multi-segment path to multipart an array id). However, in some situations you may wish to preserve the path directly as a string. You can override `parsePath` for simpler path to id preservation: ````javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } ```` @@ -444,15 +442,15 @@ When using an export resource class, the REST interface will automatically creat For example, if we had a method to post a comment on a blog, and when this happens we also want to update an array of comment IDs on the blog record, but then add the comment to a separate comment table. We might do this: -````javascript +```javascript const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -570,7 +568,7 @@ let results = Product.search({ sort: { attribute: 'price' }, }); for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -640,9 +638,9 @@ If we have extended this table class with our own `get()` we can interact with t export class CustomProduct extends Product { async get(target) { let record = await super.get(target); - let name = record.name; / this is the name of the current product - let rating = record.rating; / this is the rating of the current product - / we can't directly modify the record (it is frozen), but we can copy if we want to return a modification + let name = record.name; // this is the name of the current product + let rating = record.rating; // this is the rating of the current product + // we can't directly modify the record (it is frozen), but we can copy if we want to return a modification record = { ...record, rating: 3 }; return record; } @@ -653,9 +651,9 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -/ if we want to update a single property: +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +// if we want to update a single property: await Product.patch(1, { rating: 3 }); ``` @@ -667,7 +665,7 @@ export class CustomProduct extends Product { let record = this.update(target); record.name = data.name; record.description = data.description; - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -700,12 +698,12 @@ export class CustomProduct extends Product { let record = this.update(target); let brandName = record.brand.name; let firstVariationPrice = record.variations[0].price; - let additionalInfoOnBrand = record.brand.additionalInfo; / not defined in schema, but can still try to access property - / make some changes - record.variations.splice(0, 1); / remove first variation - record.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = record.brand.additionalInfo; // not defined in schema, but can still try to access property + // make some changes + record.variations.splice(0, 1); // remove first variation + record.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation record.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` diff --git a/site/versioned_docs/version-4.6/technical-details/reference/resources/instance-binding.md b/site/versioned_docs/version-4.6/technical-details/reference/resources/instance-binding.md index ca4b7671..9d0312b5 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/resources/instance-binding.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/resources/instance-binding.md @@ -12,22 +12,22 @@ This document describes the legacy instance binding behavior of the Resource cla export class MyExternalData extends Resource { static loadAsInstance = true; async get() { - / fetch data from an external source, using our id + // fetch data from an external source, using our id let response = await this.fetch(this.id); - / do something with the response + // do something with the response } put(data) { - / send the data into the external source + // send the data into the external source } delete() { - / delete an entity in the external data source + // delete an entity in the external data source } subscribe(options) { - / if the external data source is capable of real-time notification of changes, can subscribe + // if the external data source is capable of real-time notification of changes, can subscribe } } -/ we can export this class from resources.json as our own endpoint, or use this as the source for -/ a Harper data to store and cache the data coming from this data source: +// we can export this class from resources.json as our own endpoint, or use this as the source for +// a Harper data to store and cache the data coming from this data source: tables.MyCache.sourcedFrom(MyExternalData); ``` @@ -36,21 +36,21 @@ You can also extend table classes in the same way, overriding the instance metho ```javascript export class MyTable extends tables.MyTable { get() { - / we can add properties or change properties before returning data: + // we can add properties or change properties before returning data: this.newProperty = 'newValue'; this.existingProperty = 44; - return super.get(); / returns the record, modified with the changes above + return super.get(); // returns the record, modified with the changes above } put(data) { - / can change data any way we want + // can change data any way we want super.put(data); } delete() { super.delete(); } post(data) { - / providing a post handler (for HTTP POST requests) is a common way to create additional - / actions that aren't well described with just PUT or DELETE + // providing a post handler (for HTTP POST requests) is a common way to create additional + // actions that aren't well described with just PUT or DELETE } } ``` @@ -114,9 +114,9 @@ The query object can be used to access any query parameters that were included i ```javascript get(query) { - / note that query will only exist (as an object) if there is a query string - let param1 = query?.get?.('param1'); / returns 'value' - let id = this.getId(); / returns 'some-id' + // note that query will only exist (as an object) if there is a query string + let param1 = query?.get?.('param1'); // returns 'value' + let id = this.getId(); // returns 'some-id' ... } ``` @@ -143,7 +143,7 @@ The `query` argument is used to represent any additional query parameters that w ```javascript put(data, query) { - let param1 = query?.get?.('param1'); / returns 'value' + let param1 = query?.get?.('param1'); // returns 'value' ... } ``` @@ -272,11 +272,11 @@ This will retrieve a resource instance by id. For example, if you want to retrie ```javascript const { MyTable, Comment } = tables; ... -/ in class: +// in class: async get() { for (let commentId of this.commentIds) { let comment = await Comment.get(commentId, this); - / now you can do something with the comment record + // now you can do something with the comment record } } ``` @@ -403,17 +403,15 @@ This will be mapped to the resource with a primary key of `test?foo=bar`, and no This will return the number of records in the table. By default, this will return an approximate count of records, which is fast and efficient. If you want an exact count, you can pass `{ exactCount: true }` as the first argument, but this will be slower and more expensive. The return value will be a Promise that resolves to an object with a `recordCount` property, which is the number of records in the table. If this was not an exact count, it will also include `estimatedRange` array with estimate range of the count. -````javascript - ### `parsePath(path, context, query) {` This is called by static methods when they are responding to a URL (from HTTP request, for example), and translates the path to an id. By default, this will parse `.property` suffixes for accessing properties and specifying preferred content type in the URL (and for older tables it will convert a multi-segment path to multipart an array id). However, in some situations you may wish to preserve the path directly as a string. You can override `parsePath` for simpler path to id preservation: -````javascript +```javascript static parsePath(path) { - return path; / return the path as the id + return path; // return the path as the id } -```` +``` ### `getRecordCount: Promise<{}>` @@ -431,15 +429,15 @@ When using an export resource class, the REST interface will automatically creat For example, if we had a method to post a comment on a blog, and when this happens we also want to update an array of comment IDs on the blog record, but then add the comment to a separate comment table. We might do this: -````javascript +```javascript const { Comment } = tables; export class BlogPost extends tables.BlogPost { post(comment) { - / add a comment record to the comment table, using this resource as the source for the context + // add a comment record to the comment table, using this resource as the source for the context Comment.put(comment, this); - this.comments.push(comment.id); / add the id for the record to our array of comment ids - / Both of these actions will be committed atomically as part of the same transaction + this.comments.push(comment.id); // add the id for the record to our array of comment ids + // Both of these actions will be committed atomically as part of the same transaction } } ``` @@ -557,7 +555,7 @@ let results = Product.search({ sort: { attribute: 'price' }, }); for await (let record of results) { - / iterate through each record in the query results + // iterate through each record in the query results } ``` @@ -581,10 +579,10 @@ If we have extended this table class with our get() we can interact with any the ```javascript export class CustomProduct extends Product { get(query) { - let name = this.name; / this is the name of the current product - let rating = this.rating; / this is the rating of the current product - this.rating = 3; / we can also modify the rating for the current instance - / (with a get this won't be saved by default, but will be used when serialized) + let name = this.name; // this is the name of the current product + let rating = this.rating; // this is the rating of the current product + this.rating = 3; // we can also modify the rating for the current instance + // (with a get this won't be saved by default, but will be used when serialized) return super.get(query); } } @@ -594,17 +592,17 @@ Likewise, we can interact with resource instances in the same way when retrievin ```javascript let product1 = await Product.get(1); -let name = product1.name; / this is the name of the product with a primary key of 1 -let rating = product1.rating; / this is the rating of the product with a primary key of 1 -product1.rating = 3; / modify the rating for this instance (this will be saved without a call to update()) +let name = product1.name; // this is the name of the product with a primary key of 1 +let rating = product1.rating; // this is the rating of the product with a primary key of 1 +product1.rating = 3; // modify the rating for this instance (this will be saved without a call to update()) ``` If there are additional properties on (some) products that aren't defined in the schema, we can still access them through the resource instance, but since they aren't declared, there won't be getter/setter definition for direct property access, but we can access properties with the `get(propertyName)` method and modify properties with the `set(propertyName, value)` method: ```javascript let product1 = await Product.get(1); -let additionalInformation = product1.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema -product1.set('newProperty', 'some value'); / we can assign any properties we want with set +let additionalInformation = product1.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema +product1.set('newProperty', 'some value'); // we can assign any properties we want with set ``` And likewise, we can do this in an instance method, although you will probably want to use super.get()/set() so you don't have to write extra logic to avoid recursion: @@ -612,8 +610,8 @@ And likewise, we can do this in an instance method, although you will probably w ```javascript export class CustomProduct extends Product { get(query) { - let additionalInformation = super.get('additionalInformation'); / get the additionalInformation property value even though it isn't defined in the schema - super.set('newProperty', 'some value'); / we can assign any properties we want with set + let additionalInformation = super.get('additionalInformation'); // get the additionalInformation property value even though it isn't defined in the schema + super.set('newProperty', 'some value'); // we can assign any properties we want with set } } ``` @@ -626,7 +624,7 @@ If you want to save the changes you make, you can call the \`update()\`\` method let product1 = await Product.get(1); product1.rating = 3; product1.set('newProperty', 'some value'); -product1.update(); / save both of these property changes +product1.update(); // save both of these property changes ``` Updates are automatically saved inside modifying methods like put and post: @@ -636,7 +634,7 @@ export class CustomProduct extends Product { post(data) { this.name = data.name; this.set('description', data.description); - / both of these changes will be saved automatically as this transaction commits + // both of these changes will be saved automatically as this transaction commits } } ``` @@ -668,12 +666,12 @@ export class CustomProduct extends Product { post(data) { let brandName = this.brand.name; let firstVariationPrice = this.variations[0].price; - let additionalInfoOnBrand = this.brand.get('additionalInfo'); / not defined in schema, but can still try to access property - / make some changes - this.variations.splice(0, 1); / remove first variation - this.variations.push({ name: 'new variation', price: 9.99 }); / add a new variation + let additionalInfoOnBrand = this.brand.get('additionalInfo'); // not defined in schema, but can still try to access property + // make some changes + this.variations.splice(0, 1); // remove first variation + this.variations.push({ name: 'new variation', price: 9.99 }); // add a new variation this.brand.name = 'new brand name'; - / all these change will be saved + // all these change will be saved } } ``` @@ -692,7 +690,7 @@ You can also get "plain" object representation of a resource instance by calling let product1 = await Product.get(1); let plainObject = product1.toJSON(); for (let key in plainObject) { - / can iterate through the properties of this record + // can iterate through the properties of this record } ``` diff --git a/site/versioned_docs/version-4.6/technical-details/reference/resources/migration.md b/site/versioned_docs/version-4.6/technical-details/reference/resources/migration.md index 1d3f091e..51ec4c83 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/resources/migration.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/resources/migration.md @@ -27,15 +27,15 @@ Previous code with a `get` method: ```javascript export class MyData extends tables.MyData { async get(query) { - let id = this.getId(); / get the id + let id = this.getId(); // get the id if (query?.size > 0) { - / check number of query parameters - let idWithQuery = id + query.toString(); / add query parameters - let resource = await tables.MyData.get(idWithQuery, this); / retrieve another record - resource.newProperty = 'value'; / assign a new value to the returned resource instance + // check number of query parameters + let idWithQuery = id + query.toString(); // add query parameters + let resource = await tables.MyData.get(idWithQuery, this); // retrieve another record + resource.newProperty = 'value'; // assign a new value to the returned resource instance return resource; } else { - this.newProperty = 'value'; / assign a new value to this instance + this.newProperty = 'value'; // assign a new value to this instance return super.get(query); } } @@ -46,19 +46,19 @@ Updated code: ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior + static loadAsInstance = false; // opt in to updated behavior async get(target) { - let id = target.id; / get the id + let id = target.id; // get the id let record; if (target.size > 0) { - / check number of query parameters - let idWithQuery = target.toString(); / this is the full target with the path query parameters - / we can retrieve another record from this table directly with this.get/super.get or with tables.MyData.get + // check number of query parameters + let idWithQuery = target.toString(); // this is the full target with the path query parameters + // we can retrieve another record from this table directly with this.get/super.get or with tables.MyData.get record = await super.get(idWithQuery); } else { - record = await super.get(target); / we can just directly use the target as well + record = await super.get(target); // we can just directly use the target as well } - / the record itself is frozen, but we can copy/assign to a new object with additional properties if we want + // the record itself is frozen, but we can copy/assign to a new object with additional properties if we want return { ...record, newProperty: 'value' }; } } @@ -70,11 +70,11 @@ Previous code with a `get` method: ```javascript export class MyData extends tables.MyData { allowRead(user) { - / allow any authenticated user + // allow any authenticated user return user ? true : false; } async get(query) { - / any get logic + // any get logic return super.get(query); } } @@ -82,14 +82,14 @@ export class MyData extends tables.MyData { ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior + static loadAsInstance = false; // opt in to updated behavior async get(target) { - / While you can still use allowRead, it is not called before get is called, and it is generally encouraged - / to perform/call authorization explicitly in direct get, put, post methods rather than using allow* methods. + // While you can still use allowRead, it is not called before get is called, and it is generally encouraged + // to perform/call authorization explicitly in direct get, put, post methods rather than using allow* methods. if (!this.getContext().user) throw new Error('Unauthorized'); - target.checkPermissions = false; / authorization complete, no need to further check permissions below - / target.checkPermissions is set to true or left in place, this default get method will perform the default permissions checks - return super.get(target); / we can just directly use the query as well + target.checkPermissions = false; // authorization complete, no need to further check permissions below + // target.checkPermissions is set to true or left in place, this default get method will perform the default permissions checks + return super.get(target); // we can just directly use the query as well } } ``` @@ -102,12 +102,12 @@ export class MyData extends tables.MyData { async post(data, query) { let resource = await tables.MyData.get(data.id, this); if (resource) { - / update a property + // update a property resource.someProperty = 'value'; - / or + // or tables.MyData.patch(data.id, { someProperty: 'value' }, this); } else { - / create a new record + // create a new record MyData.create(data, this); } } @@ -118,18 +118,18 @@ Updated code: ```javascript export class MyData extends tables.MyData { - static loadAsInstance = false; / opt in to updated behavior - / IMPORTANT: arguments are reversed: + static loadAsInstance = false; // opt in to updated behavior + // IMPORTANT: arguments are reversed: async post(target, data) { let record = await this.get(data.id); if (record) { - / update a property - const updatable = await this.update(data.id); / we can alternately pass a target to update + // update a property + const updatable = await this.update(data.id); // we can alternately pass a target to update updatable.someProperty = 'value'; - / or + // or this.patch(data.id, { someProperty: 'value' }); } else { - / create a new record + // create a new record this.create(data); } } diff --git a/site/versioned_docs/version-4.6/technical-details/reference/transactions.md b/site/versioned_docs/version-4.6/technical-details/reference/transactions.md index 11a8f4dc..7e8546fb 100644 --- a/site/versioned_docs/version-4.6/technical-details/reference/transactions.md +++ b/site/versioned_docs/version-4.6/technical-details/reference/transactions.md @@ -19,7 +19,7 @@ This executes the callback in a transaction, providing a context that can be use ```javascript import { tables } from 'harperdb'; const { MyTable } = tables; -if (isMainThread) / only on main thread +if (isMainThread) // only on main thread setInterval(async () => { let someData = await (await fetch(... some URL ...)).json(); transaction((txn) => { @@ -27,7 +27,7 @@ if (isMainThread) / only on main thread MyTable.put(item, txn); } }); - }, 3600000); / every hour + }, 3600000); // every hour ``` You can provide your own context object for the transaction to attach to. If you call `transaction` with a context that already has a transaction started, it will simply use the current transaction, execute the callback and immediately return (this can be useful for ensuring that a transaction has started).