@hachej/boring-files 0.4.9 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -155,6 +155,15 @@ const revisionProvider = await connectRevisionProvider({
155
155
  });
156
156
  ```
157
157
 
158
+ The same route also answers `createResourceClient` of `@hachej/boring-files/remote`:
159
+ a resource call (`read`, `publish`, `lookup`, `rename`, `delete`) sent without the
160
+ binding header is served by `createResourceHandler` with the resolved access, so the
161
+ envelope's identity must match it, and conditional writes refuse a stale revision
162
+ with `conflict`. A binding header that is sent is checked (`409` for another
163
+ workspace); the listing and the change feed always need it. A host that serves only
164
+ `createResourceHandler` (for example at `/api/resource`) keeps working with the same
165
+ client; `connectRevisionProvider` there fails discovery (`405`), which means no listing.
166
+
158
167
  The binding discovers identity and workspace incarnation from the authenticated
159
168
  server. Reconnect when the selected workspace changes. It exposes `list`,
160
169
  filename `search`, retained `history`, `locate`, `read`, `publish`, `lookup`,
@@ -124,18 +124,24 @@ export function createRevisionHandler(options) {
124
124
  const op = new URL(request.url).searchParams.get('op');
125
125
  if (request.method === 'GET' && op === null)
126
126
  return respond(association);
127
+ // A resource call (POST without the catalog header) is `createResourceHandler`'s protocol, whose envelope carries and checks
128
+ // the identity, so a plain `createResourceClient` works here too. The listing and the change feed need the binding; a binding
129
+ // that is sent is always checked.
130
+ const resourceCall = request.method === 'POST' && !request.headers.has(catalogHeader);
127
131
  const header = request.headers.get(bindingHeader);
128
- if (!header || header.length > 16384)
129
- return refuse(403);
130
- let requested;
131
- try {
132
- requested = bindingValue(binding(JSON.parse(decodeURIComponent(header))));
133
- }
134
- catch {
132
+ if (header === null ? !resourceCall : header.length > 16384)
135
133
  return refuse(403);
134
+ if (header !== null) {
135
+ let requested;
136
+ try {
137
+ requested = bindingValue(binding(JSON.parse(decodeURIComponent(header))));
138
+ }
139
+ catch {
140
+ return refuse(403);
141
+ }
142
+ if (requested !== bindingValue(association))
143
+ return refuse(409);
136
144
  }
137
- if (requested !== bindingValue(association))
138
- return refuse(409);
139
145
  const provider = selected.provider;
140
146
  if (request.method === 'GET') {
141
147
  if (op !== 'changes')
@@ -157,7 +163,7 @@ export function createRevisionHandler(options) {
157
163
  }
158
164
  catch { /* the stream already ended */ } });
159
165
  }
160
- if (!request.headers.has(catalogHeader))
166
+ if (resourceCall)
161
167
  return await createResourceHandler({
162
168
  authenticate: async () => access,
163
169
  reader: { read: (value, context) => hold(provider.read(value, context)) },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-files",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "type": "module",