@mdbase-dev/connect 0.1.0-beta.95 → 0.1.0-beta.97

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.
@@ -1 +1 @@
1
- sha384-pt5qe5l2IuGgDjsId1oPNVT/zCR+0TLeEJNt6xKdZQSxAACXxaCqoxbZd37EriJh
1
+ sha384-qa5e6QDDjQFkjqKf4HstaDjyvJmQnvu4PkubKmMs2QIDou3qqMn+uYWQG2Z0ImA2
@@ -1,3 +1,4 @@
1
1
  import { MdbaseConnectError } from "./errors.js";
2
+ export declare function assertFreshV2AuthorizationSupport(serverUrl: string, signal?: AbortSignal): Promise<void>;
2
3
  export declare function declarationIdFromFamilyIdentity(familyIdentity: string): string;
3
4
  export declare function authorizationAbort(signal: AbortSignal, message: string, cause?: unknown): MdbaseConnectError;
@@ -1,4 +1,16 @@
1
+ import { APPLICATION_AUTHORIZATION_V2_ISSUANCE_CAPABILITY } from "@mdbase-dev/connect-protocol";
1
2
  import { MdbaseConnectError, connectError } from "./errors.js";
3
+ export async function assertFreshV2AuthorizationSupport(serverUrl, signal) {
4
+ const response = await fetch(`${serverUrl}/health`, { signal, cache: "no-store" });
5
+ const health = await response.json().catch(() => null);
6
+ if (!response.ok || !Array.isArray(health?.capabilities)
7
+ || !health.capabilities.every((value) => typeof value === "string")
8
+ || !health.capabilities.includes(APPLICATION_AUTHORIZATION_V2_ISSUANCE_CAPABILITY)) {
9
+ throw connectError("capability_contract_incompatible", "This server does not support new version-2 application authorizations.", {
10
+ details: { contract: "fresh_application_authorization", required: [2], supported: [], peer: "server" }
11
+ });
12
+ }
13
+ }
2
14
  export function declarationIdFromFamilyIdentity(familyIdentity) {
3
15
  const prefix = "bundle:";
4
16
  if (!familyIdentity.startsWith(prefix) || familyIdentity.length === prefix.length) {
package/dist/connect.js CHANGED
@@ -6,7 +6,7 @@ import { randomBase64Url } from "./base64.js";
6
6
  import { ApplicationIdentityStoreError, IndexedDbApplicationIdentityStore, MemoryApplicationIdentityStore, applicationIdentity, applicationInstallationId, signApplicationAuthorization } from "./application-identity.js";
7
7
  import { MdbaseConnection } from "./connection.js";
8
8
  import { addConnectionId, connectionIds, removeConnectionId } from "./connection-index.js";
9
- import { authorizationAbort, declarationIdFromFamilyIdentity } from "./connect-authorization-helpers.js";
9
+ import { assertFreshV2AuthorizationSupport, authorizationAbort, declarationIdFromFamilyIdentity } from "./connect-authorization-helpers.js";
10
10
  import { IndexedDbGrantKeyStore, MemoryGrantKeyStore } from "./crypto.js";
11
11
  import { MdbaseConnectError, connectError, serverConnectError } from "./errors.js";
12
12
  import { prepareAuthorizationWindow, publishPopupAuthorizationCompletion, waitForPopupAuthorization } from "./popup-authorization.js";
@@ -162,6 +162,9 @@ export class MdbaseConnectInternals {
162
162
  });
163
163
  try {
164
164
  validateAuthorizationSelection(application.requirements, options);
165
+ if (application.requirements.capabilities?.contract_version === 2) {
166
+ await assertFreshV2AuthorizationSupport(this.serverUrl, options.signal);
167
+ }
165
168
  }
166
169
  catch (error) {
167
170
  popup?.close();
@@ -187,7 +187,9 @@ export interface QueryResult<Record extends JsonObject = JsonObject> {
187
187
  };
188
188
  }
189
189
  export interface QueryPagesOptions<Record extends JsonObject = JsonObject> {
190
+ /** Initial page size. Cursor authorities pin this size for the query lifetime. */
190
191
  firstPageSize?: number;
192
+ /** Also sets the initial size unless firstPageSize is explicit; authorities may cap it. */
191
193
  pageSize?: number;
192
194
  signal?: AbortSignal;
193
195
  /** Independent budget for each page requested by this caller-driven iterator. */
@@ -196,7 +198,9 @@ export interface QueryPagesOptions<Record extends JsonObject = JsonObject> {
196
198
  onProgress?: (page: QueryPage<Record>) => void;
197
199
  }
198
200
  export interface QueryAllOptions<Record extends JsonObject = JsonObject> extends ConnectRequestOptions {
201
+ /** Initial page size. Cursor authorities pin this size for the query lifetime. */
199
202
  firstPageSize?: number;
203
+ /** Also sets the initial size unless firstPageSize is explicit; authorities may cap it. */
200
204
  pageSize?: number;
201
205
  onProgress?: (page: QueryPage<Record>) => void;
202
206
  }
@@ -3,7 +3,7 @@ import { connectFailure, connectSuccess } from "./outcomes.js";
3
3
  export async function* coordinatedQueryPages(query, releaseQueryCursor, input = {}, options = {}) {
4
4
  const { offset: requestedOffset, limit: requestedLimit, cursor: requestedCursor, pagination: requestedPagination, snapshot: requestedSnapshot, ...criteria } = input;
5
5
  let offset = nonNegativeInteger(requestedOffset, 0);
6
- const firstPageSize = positiveInteger(options.firstPageSize ?? requestedLimit, 200);
6
+ const firstPageSize = positiveInteger(options.firstPageSize ?? options.pageSize ?? requestedLimit, 200);
7
7
  const pageSize = positiveInteger(options.pageSize ?? requestedLimit, 1_000);
8
8
  let cursor = requestedCursor;
9
9
  let cursorMode = requestedCursor !== undefined;
@@ -25,7 +25,8 @@ export async function* coordinatedQueryPages(query, releaseQueryCursor, input =
25
25
  && requestedSnapshot === undefined;
26
26
  let queried = await query({
27
27
  ...criteria,
28
- limit: pageNumber === 0 ? firstPageSize : pageSize,
28
+ // Continuations use the page size pinned when the cursor was opened.
29
+ ...(!cursorMode ? { limit: pageNumber === 0 ? firstPageSize : pageSize } : {}),
29
30
  ...(cursorMode
30
31
  ? (pageCursor ? { cursor: pageCursor } : { pagination: "cursor" })
31
32
  : {
@@ -100,7 +101,9 @@ export async function* coordinatedQueryPages(query, releaseQueryCursor, input =
100
101
  }
101
102
  finally {
102
103
  if (cursorMode && cursorToRelease) {
103
- await releaseQueryCursor(cursorToRelease);
104
+ // Cleanup has its own bounded request. It must not delay delivery of
105
+ // completed pages or consume the caller's data deadline.
106
+ void releaseQueryCursor(cursorToRelease).catch(() => undefined);
104
107
  }
105
108
  }
106
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mdbase-dev/connect",
3
- "version": "0.1.0-beta.95",
3
+ "version": "0.1.0-beta.97",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  "README.md"
34
34
  ],
35
35
  "dependencies": {
36
- "@mdbase-dev/connect-protocol": "0.1.0-beta.95"
36
+ "@mdbase-dev/connect-protocol": "0.1.0-beta.97"
37
37
  },
38
38
  "devDependencies": {
39
39
  "esbuild": "^0.28.1",