deepspace 0.27.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.d.ts CHANGED
@@ -3096,8 +3096,10 @@ declare function handleDelete(ctx: RecordContext, ws: WebSocket, attachment: Con
3096
3096
  * that have already been authorized at the app level.
3097
3097
  * @param systemUpdate - When true, also skip system-managed field stripping.
3098
3098
  * Used for server-initiated updates to system fields (e.g. user profile sync).
3099
+ * @param updateOnly - When true, an absent recordId is refused instead of
3100
+ * created. `records.update`'s mode: updates never create.
3099
3101
  */
3100
- declare function putRecord(ctx: RecordContext, collection: string, recordId: string, data: Record<string, unknown>, userId: string, userRole: string, skipUserRbac?: boolean, systemUpdate?: boolean): ToolResult;
3102
+ declare function putRecord(ctx: RecordContext, collection: string, recordId: string, data: Record<string, unknown>, userId: string, userRole: string, skipUserRbac?: boolean, systemUpdate?: boolean, updateOnly?: boolean): ToolResult;
3101
3103
  /**
3102
3104
  * Delete a record. Returns ToolResult instead of sending WS messages.
3103
3105
  * Performs RBAC check and broadcasts the deletion.
package/dist/server.js CHANGED
@@ -408,10 +408,10 @@ async function writeQuotaSummary(bucket, key, previousEtag, summary) {
408
408
  });
409
409
  return object ? { etag: object.etag, summary } : null;
410
410
  }
411
- async function ensureQuotaSummary(bucket, storage, prefixes, forceMeasure = false, excludedMarkerKey) {
411
+ async function ensureQuotaSummary(bucket, storage, prefixes, measure = "reuse", excludedMarkerKey) {
412
412
  for (let attempt = 0; attempt < 4; attempt++) {
413
413
  const current = await readQuotaSummary(bucket, storage.quotaKey);
414
- if (!forceMeasure && current.summary && accountPrefixesEqual(current.summary.prefixes, prefixes)) {
414
+ if (measure !== "force" && current.summary && accountPrefixesEqual(current.summary.prefixes, prefixes) && !(measure === "repairDirty" && current.summary.dirty && Date.now() - current.summary.measuredAt >= QUOTA_REPAIR_COOLDOWN_MS)) {
415
415
  return current;
416
416
  }
417
417
  const usedBytes = await measuredAllocationUsage(bucket, storage, prefixes, excludedMarkerKey);
@@ -599,7 +599,7 @@ async function reserveQuotaSummary(bucket, storage, prefixes, initial, chargeByt
599
599
  bucket,
600
600
  storage,
601
601
  prefixes,
602
- false,
602
+ "reuse",
603
603
  markerKey ?? void 0
604
604
  );
605
605
  if (!ensured) {
@@ -624,7 +624,7 @@ async function reserveQuotaSummary(bucket, storage, prefixes, initial, chargeByt
624
624
  bucket,
625
625
  storage,
626
626
  prefixes,
627
- true,
627
+ "force",
628
628
  markerKey ?? void 0
629
629
  );
630
630
  if (!refreshed?.summary) {
@@ -717,7 +717,7 @@ async function storageUsage(bucket, storage) {
717
717
  try {
718
718
  const prefixes = await allocationPrefixes(storage);
719
719
  if (prefixes === null) return null;
720
- const current = await ensureQuotaSummary(bucket, storage, prefixes);
720
+ const current = await ensureQuotaSummary(bucket, storage, prefixes, "repairDirty");
721
721
  return current?.summary?.usedBytes ?? null;
722
722
  } catch (error) {
723
723
  console.error("[files:quota] usage:", error);
@@ -3876,7 +3876,7 @@ function handleDelete(ctx, ws, attachment, payload) {
3876
3876
  ctx.send(ws, serverBuild.error(result.error));
3877
3877
  }
3878
3878
  }
3879
- function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRbac = false, systemUpdate = false) {
3879
+ function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRbac = false, systemUpdate = false, updateOnly = false) {
3880
3880
  const resolved = resolveCollection(ctx, collection);
3881
3881
  if (!resolved.ok) return { success: false, error: resolved.error };
3882
3882
  const schema = resolved.schema;
@@ -3894,6 +3894,12 @@ function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRb
3894
3894
  }
3895
3895
  const existing = getRecord(ctx.sql, collection, recordId, schema);
3896
3896
  const isUpdate = existing !== null;
3897
+ if (updateOnly && !isUpdate) {
3898
+ return {
3899
+ success: false,
3900
+ error: `records.update: no record "${recordId}" in ${collection} \u2014 updates never create; use records.create.`
3901
+ };
3902
+ }
3897
3903
  const mergedData = isUpdate ? { ...existing.data, ...data } : { ...data };
3898
3904
  for (const col of columns) {
3899
3905
  if (col.readonly) continue;
@@ -5052,7 +5058,7 @@ async function executeTool(ctx, toolName, params, userId, userRole, skipUserRbac
5052
5058
  if (!collection) return { success: false, error: "Missing required param: collection" };
5053
5059
  if (!recordId) return { success: false, error: "Missing required param: recordId" };
5054
5060
  if (!data) return { success: false, error: "Missing required param: data" };
5055
- return putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRbac);
5061
+ return putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRbac, false, true);
5056
5062
  }
5057
5063
  case "records.delete": {
5058
5064
  const collection = params.collection;