@tinycloud/operations 0.3.2-beta.1 → 0.3.2-beta.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/coverage.json CHANGED
@@ -44,10 +44,14 @@
44
44
  { "command": "delegation info <cid>", "status": "legacy" },
45
45
  { "command": "delegation revoke <cid>", "status": "legacy" },
46
46
  { "command": "share", "status": "legacy" },
47
- { "command": "share create", "status": "legacy" },
48
- { "command": "share receive [data]", "status": "legacy" },
47
+ { "command": "share inspect [url]", "status": "legacy" },
48
+ { "command": "share migrate [url]", "status": "legacy" },
49
+ { "command": "share notify <id>", "status": "legacy" },
50
+ { "command": "share publish <files>", "status": "legacy" },
51
+ { "command": "share receive [url]", "status": "legacy" },
49
52
  { "command": "share list", "status": "legacy" },
50
- { "command": "share revoke <token>", "status": "legacy" },
53
+ { "command": "share revoke <id>", "status": "legacy" },
54
+ { "command": "share show <id>", "status": "legacy" },
51
55
  { "command": "node", "status": "legacy" },
52
56
  { "command": "node health", "status": "legacy" },
53
57
  { "command": "node version", "status": "legacy" },
@@ -170,10 +170,10 @@ var require_decrypt_transport_response_error = __commonJS({
170
170
  var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
171
171
  var DecryptTransportResponseError_exports = {};
172
172
  __export2(DecryptTransportResponseError_exports, {
173
- DecryptTransportResponseError: () => DecryptTransportResponseError4
173
+ DecryptTransportResponseError: () => DecryptTransportResponseError2
174
174
  });
175
175
  module2.exports = __toCommonJS2(DecryptTransportResponseError_exports);
176
- var DecryptTransportResponseError4 = class extends Error {
176
+ var DecryptTransportResponseError2 = class extends Error {
177
177
  constructor(status, permissionHint) {
178
178
  super("Node decrypt request failed");
179
179
  this.status = status;
@@ -209,9 +209,6 @@ var import_zod2 = require("zod");
209
209
  // ../sdk-core/src/manifest.ts
210
210
  var import_ms = __toESM(require_ms(), 1);
211
211
 
212
- // ../sdk-services/dist/index.js
213
- var import_zod = require("zod");
214
-
215
212
  // ../bootstrap/dist/index.js
216
213
  var CAPABILITIES = [
217
214
  { urn: "tinycloud.kv/get", service: "tinycloud.kv", status: "active" },
@@ -620,28 +617,6 @@ function withCapabilitiesReadForSpaces(resources) {
620
617
  }))
621
618
  ]);
622
619
  }
623
- function accountRegistryPermissions() {
624
- return [
625
- {
626
- service: "tinycloud.kv",
627
- space: ACCOUNT_REGISTRY_SPACE,
628
- path: ACCOUNT_REGISTRY_PATH,
629
- actions: [KV.GET, KV.PUT, KV.LIST]
630
- },
631
- {
632
- service: "tinycloud.kv",
633
- space: ACCOUNT_REGISTRY_SPACE,
634
- path: "spaces/",
635
- actions: [KV.GET, KV.PUT, KV.LIST]
636
- },
637
- {
638
- service: "tinycloud.sql",
639
- space: ACCOUNT_REGISTRY_SPACE,
640
- path: "account",
641
- actions: [SQL.READ, SQL.WRITE, SQL.SCHEMA]
642
- }
643
- ];
644
- }
645
620
  function composeManifestRequest(inputs, options = {}) {
646
621
  if (!Array.isArray(inputs) || inputs.length === 0) {
647
622
  throw new Error("composeManifestRequest requires at least one manifest");
@@ -655,7 +630,12 @@ function composeManifestRequest(inputs, options = {}) {
655
630
  return explicit.flatMap((entry) => expandPermissionEntry(entry, prefix, space));
656
631
  });
657
632
  if (includeAccountRegistryPermissions) {
658
- resources.push(...accountRegistryPermissions());
633
+ resources.push(
634
+ ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({
635
+ ...resource,
636
+ actions: [...resource.actions]
637
+ }))
638
+ );
659
639
  }
660
640
  const manifestsByAppId = /* @__PURE__ */ new Map();
661
641
  for (const manifest of manifests) {
@@ -683,11 +663,60 @@ function composeBootstrapSpaceManifest(space) {
683
663
  includeAccountRegistryPermissions: false
684
664
  });
685
665
  }
666
+ var ACCOUNT_MANIFEST_PERMISSIONS = Object.freeze([
667
+ {
668
+ service: "tinycloud.kv",
669
+ space: ACCOUNT_REGISTRY_SPACE,
670
+ path: ACCOUNT_REGISTRY_PATH,
671
+ actions: [KV.GET, KV.PUT, KV.LIST]
672
+ },
673
+ {
674
+ service: "tinycloud.kv",
675
+ space: ACCOUNT_REGISTRY_SPACE,
676
+ path: "spaces/",
677
+ actions: [KV.GET, KV.PUT, KV.LIST]
678
+ },
679
+ {
680
+ service: "tinycloud.kv",
681
+ space: ACCOUNT_REGISTRY_SPACE,
682
+ path: "system/bootstrap/complete",
683
+ actions: [KV.GET, KV.PUT]
684
+ },
685
+ {
686
+ service: "tinycloud.delegation",
687
+ space: ACCOUNT_REGISTRY_SPACE,
688
+ path: "",
689
+ actions: ["tinycloud.delegation/list"]
690
+ },
691
+ {
692
+ service: "tinycloud.sql",
693
+ space: ACCOUNT_REGISTRY_SPACE,
694
+ path: "account",
695
+ actions: [SQL.READ, SQL.WRITE, SQL.SCHEMA]
696
+ },
697
+ {
698
+ service: "tinycloud.capabilities",
699
+ space: ACCOUNT_REGISTRY_SPACE,
700
+ path: "",
701
+ actions: [CAPABILITIES2.READ]
702
+ }
703
+ ]);
704
+ var BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST = composeBootstrapSpaceManifest(BOOTSTRAP_DEFAULT_SPACE);
705
+ var BOOTSTRAP_WIDENED_DEFAULT_SESSION_REQUEST = {
706
+ ...BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST,
707
+ resources: [
708
+ ...BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST.resources,
709
+ ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({
710
+ ...resource,
711
+ actions: [...resource.actions]
712
+ }))
713
+ ]
714
+ };
686
715
  var BOOTSTRAP_SESSION_REQUESTS = Object.freeze(
687
716
  Object.fromEntries(
688
717
  BOOTSTRAP_SPACE_NAMES.map((space) => [
689
718
  space,
690
- composeBootstrapSpaceManifest(space)
719
+ space === BOOTSTRAP_DEFAULT_SPACE ? BOOTSTRAP_WIDENED_DEFAULT_SESSION_REQUEST : composeBootstrapSpaceManifest(space)
691
720
  ])
692
721
  )
693
722
  );
@@ -718,9 +747,8 @@ var BOOTSTRAP_ALLOWLIST = Object.freeze(
718
747
  );
719
748
 
720
749
  // ../sdk-services/dist/index.js
750
+ var import_zod = require("zod");
721
751
  var import_decrypt_transport_response_error = __toESM(require_decrypt_transport_response_error(), 1);
722
- var import_decrypt_transport_response_error2 = __toESM(require_decrypt_transport_response_error(), 1);
723
- var import_decrypt_transport_response_error3 = __toESM(require_decrypt_transport_response_error(), 1);
724
752
  var ErrorCodes = {
725
753
  // Common errors
726
754
  NOT_FOUND: "NOT_FOUND",
@@ -2261,6 +2289,7 @@ var KVService = class extends BaseService {
2261
2289
  }
2262
2290
  seen.add(path);
2263
2291
  }
2292
+ let requestMayHaveDispatched = false;
2264
2293
  try {
2265
2294
  const body = new FormData();
2266
2295
  for (let index = 0; index < items.length; index++) {
@@ -2278,14 +2307,31 @@ var KVService = class extends BaseService {
2278
2307
  action: KVAction.PUT
2279
2308
  }))
2280
2309
  );
2281
- const response = await this.context.fetch(`${this.host}/invoke`, {
2310
+ const url = `${this.host}/invoke`;
2311
+ const init = {
2282
2312
  method: "POST",
2283
2313
  headers,
2284
2314
  body,
2285
2315
  signal: this.combineSignals(options?.signal)
2286
- });
2316
+ };
2317
+ const fetchFn = this.context.fetch;
2318
+ requestMayHaveDispatched = true;
2319
+ const response = await fetchFn(url, init);
2287
2320
  if (!response.ok) {
2288
- const errorText = await response.text();
2321
+ let errorText;
2322
+ try {
2323
+ errorText = await response.text();
2324
+ } catch (textError) {
2325
+ const cause = textError instanceof Error ? textError : new Error(String(textError));
2326
+ return err(
2327
+ serviceError(
2328
+ ErrorCodes.KV_WRITE_FAILED,
2329
+ `Failed to batch put ${items.length} key(s): ${response.status} - <response body could not be read: ${cause.message}>`,
2330
+ "kv",
2331
+ { cause, meta: { status: response.status, statusText: response.statusText } }
2332
+ )
2333
+ );
2334
+ }
2289
2335
  if (response.status === 401 || response.status === 403) {
2290
2336
  const { resource, action } = parseAuthError(errorText);
2291
2337
  return err(authUnauthorizedError("kv", errorText, {
@@ -2311,19 +2357,73 @@ var KVService = class extends BaseService {
2311
2357
  )
2312
2358
  );
2313
2359
  }
2314
- const batchResponse = this.normalizeBatchPutResponse(await response.json());
2315
- if (!batchResponse || batchResponse.count !== batchResponse.written.length) {
2360
+ let rawBody;
2361
+ try {
2362
+ rawBody = await response.json();
2363
+ } catch (jsonError) {
2364
+ const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));
2365
+ return err(
2366
+ serviceError(
2367
+ ErrorCodes.NETWORK_ERROR,
2368
+ `KV batchPut response was not valid JSON: ${cause.message}`,
2369
+ "kv",
2370
+ {
2371
+ cause,
2372
+ meta: {
2373
+ requestMayHaveDispatched: true,
2374
+ responseReceived: true,
2375
+ status: response.status,
2376
+ outcome: "batch-unconfirmed"
2377
+ }
2378
+ }
2379
+ )
2380
+ );
2381
+ }
2382
+ const batchResponse = this.normalizeBatchPutResponse(rawBody);
2383
+ if (!batchResponse) {
2316
2384
  return err(
2317
2385
  serviceError(
2318
2386
  ErrorCodes.NETWORK_ERROR,
2319
2387
  "KV batchPut response did not include matching written keys and count",
2320
- "kv"
2388
+ "kv",
2389
+ {
2390
+ meta: {
2391
+ requestMayHaveDispatched: true,
2392
+ responseReceived: true,
2393
+ status: response.status,
2394
+ outcome: "batch-unconfirmed"
2395
+ }
2396
+ }
2397
+ )
2398
+ );
2399
+ }
2400
+ const requestedPaths = new Set(paths);
2401
+ const writtenPaths = new Set(batchResponse.written);
2402
+ const matchesRequest = batchResponse.count === paths.length && batchResponse.written.length === paths.length && writtenPaths.size === paths.length && batchResponse.written.every((key) => requestedPaths.has(key));
2403
+ if (!matchesRequest) {
2404
+ return err(
2405
+ serviceError(
2406
+ ErrorCodes.NETWORK_ERROR,
2407
+ `KV batchPut response did not confirm all ${paths.length} requested key(s) were written`,
2408
+ "kv",
2409
+ {
2410
+ meta: {
2411
+ requestMayHaveDispatched: true,
2412
+ responseReceived: true,
2413
+ status: response.status,
2414
+ outcome: "batch-unconfirmed"
2415
+ }
2416
+ }
2321
2417
  )
2322
2418
  );
2323
2419
  }
2324
2420
  return ok(batchResponse);
2325
2421
  } catch (error) {
2326
- return err(wrapError("kv", error));
2422
+ const wrapped = wrapError("kv", error);
2423
+ return err({
2424
+ ...wrapped,
2425
+ meta: { ...wrapped.meta, requestMayHaveDispatched }
2426
+ });
2327
2427
  }
2328
2428
  });
2329
2429
  }
@@ -6661,15 +6761,28 @@ function normalizeSpace(space) {
6661
6761
  function isCapabilitySubset(requested, granted) {
6662
6762
  const missing = [];
6663
6763
  for (const req of requested) {
6664
- const match = granted.find((g) => canonicalizeEntryMatches(req, g));
6665
- if (match === void 0) {
6764
+ const matchingScope = granted.filter(
6765
+ (entry) => canonicalizeEntryScopeMatches(req, entry)
6766
+ );
6767
+ const requestedActions = expandActionShortNames(
6768
+ req.service,
6769
+ req.actions
6770
+ );
6771
+ const grantedActions = matchingScope.flatMap(
6772
+ (entry) => expandActionShortNames(entry.service, entry.actions)
6773
+ );
6774
+ const covered = requestedActions.every(
6775
+ (requestedAction) => grantedActions.some(
6776
+ (grantedAction) => actionContains(grantedAction, requestedAction)
6777
+ )
6778
+ );
6779
+ if (!covered) {
6666
6780
  missing.push(cloneEntry(req));
6667
- continue;
6668
6781
  }
6669
6782
  }
6670
6783
  return { subset: missing.length === 0, missing };
6671
6784
  }
6672
- function canonicalizeEntryMatches(requested, granted) {
6785
+ function canonicalizeEntryScopeMatches(requested, granted) {
6673
6786
  if (requested.service !== granted.service) {
6674
6787
  return false;
6675
6788
  }
@@ -6679,15 +6792,6 @@ function canonicalizeEntryMatches(requested, granted) {
6679
6792
  if (!pathContains(granted.path, requested.path)) {
6680
6793
  return false;
6681
6794
  }
6682
- const reqActions = new Set(
6683
- expandActionShortNames(requested.service, requested.actions)
6684
- );
6685
- const grantedActions = expandActionShortNames(granted.service, granted.actions);
6686
- for (const a of reqActions) {
6687
- if (!grantedActions.some((grantedAction) => actionContains(grantedAction, a))) {
6688
- return false;
6689
- }
6690
- }
6691
6795
  if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {
6692
6796
  return false;
6693
6797
  }