or3-provider-sqlite 0.0.12 → 0.0.13

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
@@ -273,3 +273,19 @@ prints its selected path; restart it after provider edits. Missing repositories
273
273
  or failed builds fall back to installed packages with a warning.
274
274
  `OR3_LOCAL_PROVIDERS=false` disables local selection. Production builds use the
275
275
  installed package, so local development does not publish these changes.
276
+
277
+ ## Workspace Files and Trash admission
278
+
279
+ Native synchronous adapters advertise workspace-item capability v1. Canonical
280
+ posts/projects with catalog, logical Trash, or file-membership semantics require
281
+ a current writer even when the incoming update omits those fields. Admission is
282
+ checked inside the same write transaction, before versions or changes are
283
+ allocated. Canonical retained post references include logically trashed catalog
284
+ entries/documents and checkpoints. D1 does not advertise this transactional
285
+ capability.
286
+
287
+ This source change is a provider publication dependency for the host feature.
288
+ Rebuild the provider before testing a file-linked host; follow the existing OR3
289
+ release/update workflow separately. Local qualification does not authorize
290
+ publication or a production migration. Files cloud exposure remains gated until
291
+ the supported profile is qualified.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "or3-provider-sqlite",
3
3
  "configKey": "or3-provider-sqlite",
4
- "version": "0.0.12",
4
+ "version": "0.0.13",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -15,10 +15,12 @@ export declare class SqliteSyncGatewayAdapter implements SyncGatewayAdapter {
15
15
  readonly snapshotBootstrap: "snapshot-v1";
16
16
  readonly historyRetention: "snapshot-v1";
17
17
  readonly backgroundGenerationHistory?: undefined;
18
+ readonly workspaceItems?: undefined;
18
19
  } | {
19
20
  readonly snapshotBootstrap: "snapshot-v1";
20
21
  readonly historyRetention: "snapshot-v1";
21
22
  readonly backgroundGenerationHistory: "v1";
23
+ readonly workspaceItems: "v1";
22
24
  };
23
25
  private get db();
24
26
  private assertCanonicalActor;
@@ -1,3 +1,4 @@
1
+ import { hasWorkspaceItemSemantics, WORKSPACE_ITEM_CAPABILITY } from "./workspace-item-capability.js";
1
2
  import { createHash } from "node:crypto";
2
3
  import { canRunSyncHistoryGc } from "./history-gc-policy.js";
3
4
  import { createError } from "h3";
@@ -428,7 +429,8 @@ export class SqliteSyncGatewayAdapter {
428
429
  } : {
429
430
  snapshotBootstrap: "snapshot-v1",
430
431
  historyRetention: "snapshot-v1",
431
- backgroundGenerationHistory: "v1"
432
+ backgroundGenerationHistory: "v1",
433
+ workspaceItems: "v1"
432
434
  };
433
435
  }
434
436
  get db() {
@@ -1068,6 +1070,20 @@ export class SqliteSyncGatewayAdapter {
1068
1070
  let finalServerVersion = 0;
1069
1071
  const hookEmissions = [];
1070
1072
  const runTx = raw.transaction(() => {
1073
+ if (input.workspaceItemCapability !== WORKSPACE_ITEM_CAPABILITY) {
1074
+ for (const op of uniqueOps) {
1075
+ const table = SYNCED_TABLE_MAP[op.tableName];
1076
+ if (!table || !["posts", "projects"].includes(op.tableName)) continue;
1077
+ const canonical = raw.prepare("SELECT data_json FROM " + table + " WHERE id = ? AND workspace_id = ?").get(op.pk, workspaceId);
1078
+ if (hasWorkspaceItemSemantics(op.tableName, op.payload) || hasWorkspaceItemSemantics(op.tableName, canonical ? JSON.parse(canonical.data_json) : void 0)) {
1079
+ throw createError({
1080
+ statusCode: 426,
1081
+ statusMessage: "Update OR3 Chat to use workspace Files and Trash",
1082
+ data: { code: "OR3_WORKSPACE_ITEM_UPDATE_REQUIRED", requiredCapability: WORKSPACE_ITEM_CAPABILITY }
1083
+ });
1084
+ }
1085
+ }
1086
+ }
1071
1087
  const opIds = uniqueOps.map((o) => o.stamp.opId);
1072
1088
  const existingOps = /* @__PURE__ */ new Map();
1073
1089
  const chunkSize = 500;
@@ -0,0 +1,4 @@
1
+ export declare const WORKSPACE_ITEM_CAPABILITY: "v1";
2
+ export type WorkspaceItemCapability = typeof WORKSPACE_ITEM_CAPABILITY;
3
+ /** Detect semantics even when their version/content is malformed or unknown. */
4
+ export declare function hasWorkspaceItemSemantics(tableName: string, payload: unknown): boolean;
@@ -0,0 +1,26 @@
1
+ const FILE_CATALOG_POST_TYPE = "or3:file";
2
+ const WORKSPACE_ITEM_META_KEY = "or3.workspace-item";
3
+ export const WORKSPACE_ITEM_CAPABILITY = "v1";
4
+ function decoded(value) {
5
+ if (typeof value !== "string") return value;
6
+ try {
7
+ return JSON.parse(value);
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ export function hasWorkspaceItemSemantics(tableName, payload) {
13
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
14
+ const row = payload;
15
+ if (tableName === "posts") {
16
+ if ((row.post_type ?? row.postType) === FILE_CATALOG_POST_TYPE) return true;
17
+ const meta = decoded(row.meta);
18
+ if (Array.isArray(meta)) return meta.some((entry) => entry?.key === WORKSPACE_ITEM_META_KEY);
19
+ return !!meta && typeof meta === "object" && (Object.hasOwn(meta, WORKSPACE_ITEM_META_KEY) || "key" in meta && meta.key === WORKSPACE_ITEM_META_KEY);
20
+ }
21
+ if (tableName === "projects") {
22
+ const entries = decoded(row.data);
23
+ return Array.isArray(entries) && entries.some((entry) => entry && typeof entry === "object" && entry.kind === "file");
24
+ }
25
+ return false;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "or3-provider-sqlite",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Saluana/or3-provider-sqlite.git"