@tearleads/client-sdk 0.1.17 → 0.1.19

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.
Files changed (36) hide show
  1. package/client/principalMutationApi.d.ts +1 -1
  2. package/client/principalMutationApi.js +5 -13
  3. package/client/principalMutationApi.js.map +1 -1
  4. package/client/principalMutationDispatch.js +54 -0
  5. package/client/principalMutationDispatch.js.map +1 -0
  6. package/data/persistence/container-contents/documentMoveIntentPersistence.js.map +1 -1
  7. package/data/principals/principalMutationJournal.d.ts +2 -5
  8. package/data/principals/principalMutationJournal.js +3 -20
  9. package/data/principals/principalMutationJournal.js.map +1 -1
  10. package/data/principals/principalMutationJournalShape.d.ts +26 -0
  11. package/data/principals/principalMutationJournalShape.js +46 -0
  12. package/data/principals/principalMutationJournalShape.js.map +1 -0
  13. package/index.d.ts +1 -1
  14. package/index.js.map +1 -1
  15. package/internal/api-client/ApiClient.d.ts +267 -3
  16. package/internal/api-client/ApiClient.js +11 -29
  17. package/internal/api-client/ApiClient.js.map +1 -1
  18. package/internal/api-client/principalPolicyRequests.d.ts +266 -1
  19. package/internal/api-client/principalPolicyRequests.js +43 -1
  20. package/internal/api-client/principalPolicyRequests.js.map +1 -1
  21. package/internal/loro/document.js +8 -11
  22. package/internal/loro/document.js.map +1 -1
  23. package/internal/validators/operation/organizations.d.ts +54 -0
  24. package/internal/validators/operation/organizations.js +19 -4
  25. package/internal/validators/operation/organizations.js.map +1 -1
  26. package/package.json +4 -4
  27. package/workflows/organizations/index.d.ts +1 -0
  28. package/workflows/organizations/index.js.map +1 -1
  29. package/workflows/organizations/principalMutationJournalSession.d.ts +3 -3
  30. package/workflows/organizations/principalMutationJournalSession.js +2 -2
  31. package/workflows/organizations/principalMutationJournalSession.js.map +1 -1
  32. package/workflows/organizations/principalMutationReceipt.js +13 -12
  33. package/workflows/organizations/principalMutationReceipt.js.map +1 -1
  34. package/workflows/organizations/principalOperationReceipt.d.ts +5 -0
  35. package/workflows/organizations/principalOperationReceipt.js +36 -0
  36. package/workflows/organizations/principalOperationReceipt.js.map +1 -0
@@ -17,7 +17,7 @@ interface PrincipalMutationApiCustodyInput {
17
17
  readonly readScope: () => PrincipalMutationRuntimeScope | null;
18
18
  readonly principalMutationTimeoutMs?: number | undefined;
19
19
  }
20
- /** Keep API methods bound to their real receiver and journal only policy writes. */
20
+ /** Keep API methods bound to their real receiver and journal principal policy and group lifecycle writes. */
21
21
  export declare function createPrincipalMutationApiCustody(input: PrincipalMutationApiCustodyInput): {
22
22
  bind(): PrincipalMutationApi;
23
23
  };
@@ -1,6 +1,7 @@
1
1
  import { abandonJournaledPrincipalMutation, discardUnreadableJournaledPrincipalMutation, readJournaledPrincipalMutation, } from "../workflows/organizations/principalMutationJournalManagement.js";
2
- import { recoverJournaledPrincipalMutation, submitJournaledPrincipalMutation, } from "../workflows/organizations/principalMutationJournalSession.js";
2
+ import { recoverJournaledPrincipalMutation } from "../workflows/organizations/principalMutationJournalSession.js";
3
3
  import { createPrincipalMutationDispatcher } from "./principalMutationDeadline.js";
4
+ import { createJournaledPrincipalMutations, dispatchAuthoredPrincipalMutation, } from "./principalMutationDispatch.js";
4
5
  function sameScope(current, bound) {
5
6
  if (!current || !bound)
6
7
  return current === bound;
@@ -27,7 +28,7 @@ function mutationContext(input, scope, organizationId) {
27
28
  stillCurrent: () => sameScope(input.readScope(), scope),
28
29
  };
29
30
  }
30
- /** Keep API methods bound to their real receiver and journal only policy writes. */
31
+ /** Keep API methods bound to their real receiver and journal principal policy and group lifecycle writes. */
31
32
  export function createPrincipalMutationApiCustody(input) {
32
33
  const dispatch = createPrincipalMutationDispatcher(input.principalMutationTimeoutMs);
33
34
  let cached = null;
@@ -37,11 +38,6 @@ export function createPrincipalMutationApiCustody(input) {
37
38
  if (cached && sameScope(scope, cached.scope))
38
39
  return cached.api;
39
40
  const context = (organizationId) => mutationContext(input, scope, organizationId);
40
- const commit = async (organizationId, groupId, request, options) => submitJournaledPrincipalMutation({
41
- ...context(organizationId),
42
- mutation: { groupId, request },
43
- submit: (mutation) => dispatch(options, (dispatchOptions) => input.api.commitOrganizationGroupPolicyResult(organizationId, mutation.groupId, mutation.request, dispatchOptions)),
44
- });
45
41
  const read = async (organizationId) => scope
46
42
  ? readJournaledPrincipalMutation(context(organizationId))
47
43
  : null;
@@ -51,6 +47,7 @@ export function createPrincipalMutationApiCustody(input) {
51
47
  acknowledgeUnknownOutcome,
52
48
  });
53
49
  const methods = {
50
+ ...createJournaledPrincipalMutations(input.api, context, dispatch),
54
51
  discardUnreadablePrincipalMutation: async (organizationId, recordId, acknowledgeUnknownOutcome) => discardUnreadableJournaledPrincipalMutation({
55
52
  ...context(organizationId),
56
53
  recordId,
@@ -58,15 +55,10 @@ export function createPrincipalMutationApiCustody(input) {
58
55
  }),
59
56
  readPendingPrincipalMutation: read,
60
57
  abandonPendingPrincipalMutation: abandon,
61
- commitOrganizationGroupPolicyResult: commit,
62
- commitOrganizationGroupPolicy: async (...args) => {
63
- const result = await commit(...args);
64
- return result.ok ? result.data : null;
65
- },
66
58
  recoverPendingPrincipalMutation: async (organizationId) => {
67
59
  await recoverJournaledPrincipalMutation({
68
60
  ...context(organizationId),
69
- submit: (mutation) => dispatch({ reportErrors: false }, (dispatchOptions) => input.api.commitOrganizationGroupPolicyResult(organizationId, mutation.groupId, mutation.request, dispatchOptions)),
61
+ submit: (mutation) => dispatch({ reportErrors: false }, (dispatchOptions) => dispatchAuthoredPrincipalMutation(input.api, organizationId, mutation, dispatchOptions)),
70
62
  });
71
63
  },
72
64
  };
@@ -1 +1 @@
1
- {"version":3,"file":"principalMutationApi.js","sourceRoot":"","sources":["principalMutationApi.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,iCAAiC,EACjC,2CAA2C,EAE3C,8BAA8B,GAC/B,MAAM,+DAA+D,CAAC;AACvE,OAAO,EACL,iCAAiC,EACjC,gCAAgC,GACjC,MAAM,4DAA4D,CAAC;AACpE,OAAO,EAAE,iCAAiC,EAAE,MAAM,6BAA6B,CAAC;AAYhF,SAAS,SAAS,CAChB,OAA6C,EAC7C,KAA2C;IAE3C,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,KAAK,KAAK,CAAC;IACjD,OAAO,CACL,OAAO,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;QACnC,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;QACjC,OAAO,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU;QACvC,OAAO,CAAC,mBAAmB,KAAK,KAAK,CAAC,mBAAmB;QACzD,OAAO,CAAC,kBAAkB,KAAK,KAAK,CAAC,kBAAkB;QACvD,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC/C,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAChC,CAAC;AACJ,CAAC;AAeD,SAAS,eAAe,CACtB,KAAuC,EACvC,KAA2C,EAC3C,cAAsB;IAEtB,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,KAAK,CACb,+GAA+G,CAChH,CAAC;IACJ,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,KAAK,EAAE;YACL,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;YAC9C,cAAc;YACd,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;YAC5C,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB;QACD,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,KAAK,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,iCAAiC,CAC/C,KAAuC;IAEvC,MAAM,QAAQ,GAAG,iCAAiC,CAChD,KAAK,CAAC,0BAA0B,CACjC,CAAC;IACF,IAAI,MAAM,GAGC,IAAI,CAAC;IAChB,OAAO;QACL,IAAI;YACF,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,MAAM,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;gBAAE,OAAO,MAAM,CAAC,GAAG,CAAC;YAChE,MAAM,OAAO,GAAG,CAAC,cAAsB,EAAE,EAAE,CACzC,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;YAChD,MAAM,MAAM,GAAqD,KAAK,EACpE,cAAc,EACd,OAAO,EACP,OAAO,EACP,OAAO,EACP,EAAE,CACF,gCAAgC,CAAC;gBAC/B,GAAG,OAAO,CAAC,cAAc,CAAC;gBAC1B,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;gBAC9B,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CACnB,QAAQ,CAAC,OAAO,EAAE,CAAC,eAAe,EAAE,EAAE,CACpC,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAC3C,cAAc,EACd,QAAQ,CAAC,OAAO,EAChB,QAAQ,CAAC,OAAO,EAChB,eAAe,CAChB,CACF;aACJ,CAAC,CAAC;YACL,MAAM,IAAI,GACR,KAAK,EAAE,cAAc,EAAE,EAAE,CACvB,KAAK;gBACH,CAAC,CAAC,8BAA8B,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;gBACzD,CAAC,CAAC,IAAI,CAAC;YACb,MAAM,OAAO,GACX,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,yBAAyB,EAAE,EAAE,CAC5D,iCAAiC,CAAC;gBAChC,GAAG,OAAO,CAAC,cAAc,CAAC;gBAC1B,QAAQ;gBACR,yBAAyB;aAC1B,CAAC,CAAC;YACP,MAAM,OAAO,GAAG;gBACd,kCAAkC,EAAE,KAAK,EACvC,cAAc,EACd,QAAQ,EACR,yBAAyB,EACzB,EAAE,CACF,2CAA2C,CAAC;oBAC1C,GAAG,OAAO,CAAC,cAAc,CAAC;oBAC1B,QAAQ;oBACR,yBAAyB;iBAC1B,CAAC;gBACJ,4BAA4B,EAAE,IAAI;gBAClC,+BAA+B,EAAE,OAAO;gBACxC,mCAAmC,EAAE,MAAM;gBAC3C,6BAA6B,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE;oBAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;oBACrC,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;gBACxC,CAAC;gBACD,+BAA+B,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;oBACxD,MAAM,iCAAiC,CAAC;wBACtC,GAAG,OAAO,CAAC,cAAc,CAAC;wBAC1B,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CACnB,QAAQ,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,CAAC,eAAe,EAAE,EAAE,CACpD,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAC3C,cAAc,EACd,QAAQ,CAAC,OAAO,EAChB,QAAQ,CAAC,OAAO,EAChB,eAAe,CAChB,CACF;qBACJ,CAAC,CAAC;gBACL,CAAC;aAC0B,CAAC;YAC9B,MAAM,GAAG,GAAG,mBAAmB,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YACxB,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAC1B,GAAc,EACd,OAA0B;IAE1B,MAAM,YAAY,GAAG,IAAI,GAAG,EAGzB,CAAC;IACJ,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,GAAG,EAAE;QAC3B,GAAG,CAAC,MAAM,EAAE,QAAQ;YAClB,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;gBAClC,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACxC,MAAM,KAAK,GAAY,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC7D,IAAI,OAAO,KAAK,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,QAAQ,EAAE,MAAM,KAAK,KAAK;gBAAE,OAAO,QAAQ,CAAC,KAAK,CAAC;YACtD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACjC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC,CAAC;IACH,6EAA6E;IAC7E,uEAAuE;IACvE,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAc;IACxC,OAAO,CACL,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,8BAA8B,CAAC,KAAK,UAAU;QACtE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,iCAAiC,CAAC,KAAK,UAAU;QACzE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,iCAAiC,CAAC,KAAK,UAAU;QACzE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,oCAAoC,CAAC,KAAK,UAAU,CAC7E,CAAC;AACJ,CAAC","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { ExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n abandonJournaledPrincipalMutation,\n discardUnreadableJournaledPrincipalMutation,\n type PrincipalMutationRecoveryApi,\n readJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalManagement\";\nimport {\n recoverJournaledPrincipalMutation,\n submitJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalSession\";\nimport { createPrincipalMutationDispatcher } from \"./principalMutationDeadline\";\n\nexport interface PrincipalMutationRuntimeScope {\n readonly database: object;\n readonly execSql: ExecSql;\n readonly generation: number;\n readonly identityTrustDomain: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly userId: string;\n}\n\nfunction sameScope(\n current: PrincipalMutationRuntimeScope | null,\n bound: PrincipalMutationRuntimeScope | null,\n): boolean {\n if (!current || !bound) return current === bound;\n return (\n current.database === bound.database &&\n current.execSql === bound.execSql &&\n current.generation === bound.generation &&\n current.identityTrustDomain === bound.identityTrustDomain &&\n current.signingFingerprint === bound.signingFingerprint &&\n current.signingKeyPair === bound.signingKeyPair &&\n current.userId === bound.userId\n );\n}\n\nexport type PrincipalMutationApi = ApiClient & PrincipalMutationRecoveryApi;\ntype MutationOverrides = Pick<\n ApiClient,\n \"commitOrganizationGroupPolicy\" | \"commitOrganizationGroupPolicyResult\"\n> &\n PrincipalMutationRecoveryApi;\n\ninterface PrincipalMutationApiCustodyInput {\n readonly api: ApiClient;\n readonly readScope: () => PrincipalMutationRuntimeScope | null;\n readonly principalMutationTimeoutMs?: number | undefined;\n}\n\nfunction mutationContext(\n input: PrincipalMutationApiCustodyInput,\n scope: PrincipalMutationRuntimeScope | null,\n organizationId: string,\n) {\n if (!scope)\n throw new Error(\n \"Principal mutations require an authenticated signing identity, a trusted API origin and durable local storage\",\n );\n return {\n execSql: scope.execSql,\n signingKeyPair: scope.signingKeyPair,\n scope: {\n identityTrustDomain: scope.identityTrustDomain,\n organizationId,\n signingFingerprint: scope.signingFingerprint,\n userId: scope.userId,\n },\n stillCurrent: () => sameScope(input.readScope(), scope),\n };\n}\n\n/** Keep API methods bound to their real receiver and journal only policy writes. */\nexport function createPrincipalMutationApiCustody(\n input: PrincipalMutationApiCustodyInput,\n) {\n const dispatch = createPrincipalMutationDispatcher(\n input.principalMutationTimeoutMs,\n );\n let cached: {\n scope: PrincipalMutationRuntimeScope | null;\n api: PrincipalMutationApi;\n } | null = null;\n return {\n bind(): PrincipalMutationApi {\n const scope = input.readScope();\n if (cached && sameScope(scope, cached.scope)) return cached.api;\n const context = (organizationId: string) =>\n mutationContext(input, scope, organizationId);\n const commit: ApiClient[\"commitOrganizationGroupPolicyResult\"] = async (\n organizationId,\n groupId,\n request,\n options,\n ) =>\n submitJournaledPrincipalMutation({\n ...context(organizationId),\n mutation: { groupId, request },\n submit: (mutation) =>\n dispatch(options, (dispatchOptions) =>\n input.api.commitOrganizationGroupPolicyResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n dispatchOptions,\n ),\n ),\n });\n const read: PrincipalMutationRecoveryApi[\"readPendingPrincipalMutation\"] =\n async (organizationId) =>\n scope\n ? readJournaledPrincipalMutation(context(organizationId))\n : null;\n const abandon: PrincipalMutationRecoveryApi[\"abandonPendingPrincipalMutation\"] =\n async (organizationId, mutation, acknowledgeUnknownOutcome) =>\n abandonJournaledPrincipalMutation({\n ...context(organizationId),\n mutation,\n acknowledgeUnknownOutcome,\n });\n const methods = {\n discardUnreadablePrincipalMutation: async (\n organizationId,\n recordId,\n acknowledgeUnknownOutcome,\n ) =>\n discardUnreadableJournaledPrincipalMutation({\n ...context(organizationId),\n recordId,\n acknowledgeUnknownOutcome,\n }),\n readPendingPrincipalMutation: read,\n abandonPendingPrincipalMutation: abandon,\n commitOrganizationGroupPolicyResult: commit,\n commitOrganizationGroupPolicy: async (...args) => {\n const result = await commit(...args);\n return result.ok ? result.data : null;\n },\n recoverPendingPrincipalMutation: async (organizationId) => {\n await recoverJournaledPrincipalMutation({\n ...context(organizationId),\n submit: (mutation) =>\n dispatch({ reportErrors: false }, (dispatchOptions) =>\n input.api.commitOrganizationGroupPolicyResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n dispatchOptions,\n ),\n ),\n });\n },\n } satisfies MutationOverrides;\n const api = bindMutationMethods(input.api, methods);\n cached = { scope, api };\n return api;\n },\n };\n}\n\nfunction bindMutationMethods(\n api: ApiClient,\n methods: MutationOverrides,\n): PrincipalMutationApi {\n const boundMethods = new Map<\n PropertyKey,\n { source: unknown; bound: unknown }\n >();\n const bound = new Proxy(api, {\n get(target, property) {\n if (Object.hasOwn(methods, property))\n return Reflect.get(methods, property);\n const value: unknown = Reflect.get(target, property, target);\n if (typeof value !== \"function\") return value;\n const previous = boundMethods.get(property);\n if (previous?.source === value) return previous.bound;\n const bound = value.bind(target);\n boundMethods.set(property, { source: value, bound });\n return bound;\n },\n });\n // The checked overrides supply the signatures; verify their presence through\n // the reflective boundary before exposing the extended API to callers.\n if (!hasRecoveryMethods(bound))\n throw new Error(\"Principal mutation recovery methods are unavailable\");\n return bound;\n}\n\nfunction hasRecoveryMethods(api: ApiClient): api is PrincipalMutationApi {\n return (\n typeof Reflect.get(api, \"readPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"recoverPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"abandonPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"discardUnreadablePrincipalMutation\") === \"function\"\n );\n}\n"]}
1
+ {"version":3,"file":"principalMutationApi.js","sourceRoot":"","sources":["principalMutationApi.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { ExecSql } from \"../data/sqlite/sqlSchema\";\nimport {\n abandonJournaledPrincipalMutation,\n discardUnreadableJournaledPrincipalMutation,\n type PrincipalMutationRecoveryApi,\n readJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalManagement\";\nimport { recoverJournaledPrincipalMutation } from \"../workflows/organizations/principalMutationJournalSession\";\nimport { createPrincipalMutationDispatcher } from \"./principalMutationDeadline\";\nimport {\n createJournaledPrincipalMutations,\n dispatchAuthoredPrincipalMutation,\n type JournaledPrincipalMutationMethods,\n} from \"./principalMutationDispatch\";\n\nexport interface PrincipalMutationRuntimeScope {\n readonly database: object;\n readonly execSql: ExecSql;\n readonly generation: number;\n readonly identityTrustDomain: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly userId: string;\n}\n\nfunction sameScope(\n current: PrincipalMutationRuntimeScope | null,\n bound: PrincipalMutationRuntimeScope | null,\n): boolean {\n if (!current || !bound) return current === bound;\n return (\n current.database === bound.database &&\n current.execSql === bound.execSql &&\n current.generation === bound.generation &&\n current.identityTrustDomain === bound.identityTrustDomain &&\n current.signingFingerprint === bound.signingFingerprint &&\n current.signingKeyPair === bound.signingKeyPair &&\n current.userId === bound.userId\n );\n}\n\nexport type PrincipalMutationApi = ApiClient & PrincipalMutationRecoveryApi;\ntype MutationOverrides = JournaledPrincipalMutationMethods &\n PrincipalMutationRecoveryApi;\n\ninterface PrincipalMutationApiCustodyInput {\n readonly api: ApiClient;\n readonly readScope: () => PrincipalMutationRuntimeScope | null;\n readonly principalMutationTimeoutMs?: number | undefined;\n}\n\nfunction mutationContext(\n input: PrincipalMutationApiCustodyInput,\n scope: PrincipalMutationRuntimeScope | null,\n organizationId: string,\n) {\n if (!scope)\n throw new Error(\n \"Principal mutations require an authenticated signing identity, a trusted API origin and durable local storage\",\n );\n return {\n execSql: scope.execSql,\n signingKeyPair: scope.signingKeyPair,\n scope: {\n identityTrustDomain: scope.identityTrustDomain,\n organizationId,\n signingFingerprint: scope.signingFingerprint,\n userId: scope.userId,\n },\n stillCurrent: () => sameScope(input.readScope(), scope),\n };\n}\n\n/** Keep API methods bound to their real receiver and journal principal policy and group lifecycle writes. */\nexport function createPrincipalMutationApiCustody(\n input: PrincipalMutationApiCustodyInput,\n) {\n const dispatch = createPrincipalMutationDispatcher(\n input.principalMutationTimeoutMs,\n );\n let cached: {\n scope: PrincipalMutationRuntimeScope | null;\n api: PrincipalMutationApi;\n } | null = null;\n return {\n bind(): PrincipalMutationApi {\n const scope = input.readScope();\n if (cached && sameScope(scope, cached.scope)) return cached.api;\n const context = (organizationId: string) =>\n mutationContext(input, scope, organizationId);\n const read: PrincipalMutationRecoveryApi[\"readPendingPrincipalMutation\"] =\n async (organizationId) =>\n scope\n ? readJournaledPrincipalMutation(context(organizationId))\n : null;\n const abandon: PrincipalMutationRecoveryApi[\"abandonPendingPrincipalMutation\"] =\n async (organizationId, mutation, acknowledgeUnknownOutcome) =>\n abandonJournaledPrincipalMutation({\n ...context(organizationId),\n mutation,\n acknowledgeUnknownOutcome,\n });\n const methods = {\n ...createJournaledPrincipalMutations(input.api, context, dispatch),\n discardUnreadablePrincipalMutation: async (\n organizationId,\n recordId,\n acknowledgeUnknownOutcome,\n ) =>\n discardUnreadableJournaledPrincipalMutation({\n ...context(organizationId),\n recordId,\n acknowledgeUnknownOutcome,\n }),\n readPendingPrincipalMutation: read,\n abandonPendingPrincipalMutation: abandon,\n recoverPendingPrincipalMutation: async (organizationId) => {\n await recoverJournaledPrincipalMutation({\n ...context(organizationId),\n submit: (mutation) =>\n dispatch({ reportErrors: false }, (dispatchOptions) =>\n dispatchAuthoredPrincipalMutation(\n input.api,\n organizationId,\n mutation,\n dispatchOptions,\n ),\n ),\n });\n },\n } satisfies MutationOverrides;\n const api = bindMutationMethods(input.api, methods);\n cached = { scope, api };\n return api;\n },\n };\n}\n\nfunction bindMutationMethods(\n api: ApiClient,\n methods: MutationOverrides,\n): PrincipalMutationApi {\n const boundMethods = new Map<\n PropertyKey,\n { source: unknown; bound: unknown }\n >();\n const bound = new Proxy(api, {\n get(target, property) {\n if (Object.hasOwn(methods, property))\n return Reflect.get(methods, property);\n const value: unknown = Reflect.get(target, property, target);\n if (typeof value !== \"function\") return value;\n const previous = boundMethods.get(property);\n if (previous?.source === value) return previous.bound;\n const bound = value.bind(target);\n boundMethods.set(property, { source: value, bound });\n return bound;\n },\n });\n // The checked overrides supply the signatures; verify their presence through\n // the reflective boundary before exposing the extended API to callers.\n if (!hasRecoveryMethods(bound))\n throw new Error(\"Principal mutation recovery methods are unavailable\");\n return bound;\n}\n\nfunction hasRecoveryMethods(api: ApiClient): api is PrincipalMutationApi {\n return (\n typeof Reflect.get(api, \"readPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"recoverPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"abandonPendingPrincipalMutation\") === \"function\" &&\n typeof Reflect.get(api, \"discardUnreadablePrincipalMutation\") === \"function\"\n );\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import { isCommitOrganizationGroupPolicyResponse, isCreateOrganizationGroupResponse, isDeleteOrganizationGroupResponse, isPrincipalPolicyMutationResponse, } from "../internal/validators/response/index.js";
2
+ import { submitJournaledPrincipalMutation, } from "../workflows/organizations/principalMutationJournalSession.js";
3
+ /** Dispatch the authenticated operation, with no application planning callback. */
4
+ export function dispatchAuthoredPrincipalMutation(api, organizationId, mutation, options = {}) {
5
+ switch (mutation.kind) {
6
+ case "organization":
7
+ return api.putPrincipalPolicyResult("organization", organizationId, mutation.request, options);
8
+ case "group-create":
9
+ return api.createOrganizationGroupResult(organizationId, mutation.request, options);
10
+ case "group-delete":
11
+ return api.deleteOrganizationGroupResult(organizationId, mutation.groupId, mutation.request, options);
12
+ case undefined:
13
+ return api.commitOrganizationGroupPolicyResult(organizationId, mutation.groupId, mutation.request, options);
14
+ default:
15
+ return unsupportedMutation(mutation);
16
+ }
17
+ }
18
+ function unsupportedMutation(_mutation) {
19
+ throw new Error("Unsupported principal mutation operation");
20
+ }
21
+ export function createJournaledPrincipalMutations(api, context, dispatch) {
22
+ async function submit(organizationId, mutation, validator, options = {}) {
23
+ const result = await submitJournaledPrincipalMutation({
24
+ ...context(organizationId),
25
+ mutation,
26
+ submit: (saved) => dispatch(options, (dispatchOptions) => dispatchAuthoredPrincipalMutation(api, organizationId, saved, dispatchOptions)),
27
+ });
28
+ if (!result.ok)
29
+ return result;
30
+ // The session already verified this operation before clearing its row.
31
+ // This check narrows the union for each existing public API signature.
32
+ if (!validator(result.data))
33
+ throw new Error("Unexpected principal receipt operation");
34
+ return { ok: true, data: result.data };
35
+ }
36
+ const commit = (organizationId, groupId, request, options) => submit(organizationId, { groupId, request }, isCommitOrganizationGroupPolicyResponse, options);
37
+ const create = (organizationId, request, options) => submit(organizationId, { kind: "group-create", groupId: request.groupId, request }, isCreateOrganizationGroupResponse, options);
38
+ const remove = (organizationId, groupId, request, options) => submit(organizationId, { kind: "group-delete", groupId, request }, isDeleteOrganizationGroupResponse, options);
39
+ const put = (_type, organizationId, request, options) => submit(organizationId, { kind: "organization", groupId: null, request }, isPrincipalPolicyMutationResponse, options);
40
+ return {
41
+ commitOrganizationGroupPolicyResult: commit,
42
+ commitOrganizationGroupPolicy: async (...args) => nullable(await commit(...args)),
43
+ createOrganizationGroupResult: create,
44
+ createOrganizationGroup: async (...args) => nullable(await create(...args)),
45
+ deleteOrganizationGroupResult: remove,
46
+ deleteOrganizationGroup: async (...args) => nullable(await remove(...args)),
47
+ putPrincipalPolicyResult: put,
48
+ putPrincipalPolicy: async (...args) => nullable(await put(...args)),
49
+ };
50
+ }
51
+ function nullable(result) {
52
+ return result.ok ? result.data : null;
53
+ }
54
+ //# sourceMappingURL=principalMutationDispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalMutationDispatch.js","sourceRoot":"","sources":["principalMutationDispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ApiClient,\n RequestResult,\n RequestResultOptions,\n} from \"@tearleads/api-client\";\nimport {\n isCommitOrganizationGroupPolicyResponse,\n isCreateOrganizationGroupResponse,\n isDeleteOrganizationGroupResponse,\n isPrincipalPolicyMutationResponse,\n} from \"@tearleads/validators/response\";\nimport type { AuthoredPrincipalMutation } from \"../data/principals/principalMutationJournal\";\nimport {\n type PrincipalMutationJournalContext,\n submitJournaledPrincipalMutation,\n} from \"../workflows/organizations/principalMutationJournalSession\";\nimport type { PrincipalMutationResponse } from \"../workflows/organizations/principalOperationReceipt\";\n\nimport type { createPrincipalMutationDispatcher } from \"./principalMutationDeadline\";\n\nexport type JournaledPrincipalMutationMethods = Pick<\n ApiClient,\n | \"commitOrganizationGroupPolicy\"\n | \"commitOrganizationGroupPolicyResult\"\n | \"createOrganizationGroup\"\n | \"createOrganizationGroupResult\"\n | \"deleteOrganizationGroup\"\n | \"deleteOrganizationGroupResult\"\n | \"putPrincipalPolicy\"\n | \"putPrincipalPolicyResult\"\n>;\n\n/** Dispatch the authenticated operation, with no application planning callback. */\nexport function dispatchAuthoredPrincipalMutation(\n api: ApiClient,\n organizationId: string,\n mutation: AuthoredPrincipalMutation,\n options: RequestResultOptions = {},\n): Promise<RequestResult<PrincipalMutationResponse>> {\n switch (mutation.kind) {\n case \"organization\":\n return api.putPrincipalPolicyResult(\n \"organization\",\n organizationId,\n mutation.request,\n options,\n );\n case \"group-create\":\n return api.createOrganizationGroupResult(\n organizationId,\n mutation.request,\n options,\n );\n case \"group-delete\":\n return api.deleteOrganizationGroupResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n options,\n );\n case undefined:\n return api.commitOrganizationGroupPolicyResult(\n organizationId,\n mutation.groupId,\n mutation.request,\n options,\n );\n default:\n return unsupportedMutation(mutation);\n }\n}\n\nfunction unsupportedMutation(_mutation: never): never {\n throw new Error(\"Unsupported principal mutation operation\");\n}\n\nexport function createJournaledPrincipalMutations(\n api: ApiClient,\n context: (\n organizationId: string,\n ) => Omit<PrincipalMutationJournalContext, \"submit\">,\n dispatch: ReturnType<typeof createPrincipalMutationDispatcher>,\n): JournaledPrincipalMutationMethods {\n async function submit<T extends PrincipalMutationResponse>(\n organizationId: string,\n mutation: AuthoredPrincipalMutation,\n validator: (value: unknown) => value is T,\n options: RequestResultOptions = {},\n ): Promise<RequestResult<T>> {\n const result = await submitJournaledPrincipalMutation({\n ...context(organizationId),\n mutation,\n submit: (saved) =>\n dispatch(options, (dispatchOptions) =>\n dispatchAuthoredPrincipalMutation(\n api,\n organizationId,\n saved,\n dispatchOptions,\n ),\n ),\n });\n if (!result.ok) return result;\n // The session already verified this operation before clearing its row.\n // This check narrows the union for each existing public API signature.\n if (!validator(result.data))\n throw new Error(\"Unexpected principal receipt operation\");\n return { ok: true, data: result.data };\n }\n const commit: ApiClient[\"commitOrganizationGroupPolicyResult\"] = (\n organizationId,\n groupId,\n request,\n options,\n ) =>\n submit(\n organizationId,\n { groupId, request },\n isCommitOrganizationGroupPolicyResponse,\n options,\n );\n const create: ApiClient[\"createOrganizationGroupResult\"] = (\n organizationId,\n request,\n options,\n ) =>\n submit(\n organizationId,\n { kind: \"group-create\", groupId: request.groupId, request },\n isCreateOrganizationGroupResponse,\n options,\n );\n const remove: ApiClient[\"deleteOrganizationGroupResult\"] = (\n organizationId,\n groupId,\n request,\n options,\n ) =>\n submit(\n organizationId,\n { kind: \"group-delete\", groupId, request },\n isDeleteOrganizationGroupResponse,\n options,\n );\n const put: ApiClient[\"putPrincipalPolicyResult\"] = (\n _type,\n organizationId,\n request,\n options,\n ) =>\n submit(\n organizationId,\n { kind: \"organization\", groupId: null, request },\n isPrincipalPolicyMutationResponse,\n options,\n );\n return {\n commitOrganizationGroupPolicyResult: commit,\n commitOrganizationGroupPolicy: async (...args) =>\n nullable(await commit(...args)),\n createOrganizationGroupResult: create,\n createOrganizationGroup: async (...args) => nullable(await create(...args)),\n deleteOrganizationGroupResult: remove,\n deleteOrganizationGroup: async (...args) => nullable(await remove(...args)),\n putPrincipalPolicyResult: put,\n putPrincipalPolicy: async (...args) => nullable(await put(...args)),\n };\n}\n\nfunction nullable<T>(result: RequestResult<T>): T | null {\n return result.ok ? result.data : null;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"documentMoveIntentPersistence.js","sourceRoot":"","sources":["documentMoveIntentPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n containerTables,\n documentMoveIntents,\n documentMoveIntentTables,\n documentProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentLinkIntent,\n enqueueDocumentMoveIntent,\n enqueueDocumentUnlinkIntent,\n loadDocumentIntentLinkTargets,\n} from \"./documentPlacementIntentEnqueue\";\n\nexport const DOCUMENT_MOVE_INTENT_TYPE = \"document.move\";\nexport const DOCUMENT_LINK_INTENT_TYPE = \"document.link\";\nconst DOCUMENT_PLACEMENT_INTENT_TYPES = [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n];\n\n// Resolves the organization a parked move belongs to, in confidence order:\n// the move's target container, the document's preserved projection\n// attribution, then the container the document currently sits in. Normal\n// saves routinely leave projection attribution empty (deferred-tail\n// reporting resolves through the same container join), so the container rows\n// carry the common case; rows with no resolvable organization stay in every\n// scope (device-first shapes, matching orphan priming).\nconst DENIED_INTENT_ORGANIZATION_SQL = `COALESCE(\n NULLIF(target_container.organization_id, ''),\n NULLIF(projection.organization_id, ''),\n NULLIF(projection_container.organization_id, '')\n)`;\n\nconst DENIED_INTENT_ORGANIZATION_JOINS_SQL = `\n LEFT JOIN containers target_container\n ON target_container.id = intent.target_container_id\n LEFT JOIN document_projection projection\n ON projection.local_id = intent.local_id\n LEFT JOIN containers projection_container\n ON projection_container.id = projection.container_id`;\n\n/**\n * - `pending`: replays on every structural pass.\n * - `blocked`: the last pass could not proceed locally (missing local\n * document / destination); replays, since hydration can heal it.\n * - `denied`: parked on a 403 until the access-restored replay (row 7).\n * - `unavailable`: terminal — after a projection refresh the destination\n * itself is proven deleted (coded 404 on its own projection probe). No\n * mutation 409 is ever terminal: with the destination live it can only\n * name an ancestor or source, and the next pass's fresh paths outrun it.\n * Container ids never return, so no replay can commit the intent as\n * written; it leaves the replay set until the local container tombstone\n * cascade retargets it or the user re-enqueues the move.\n */\nexport type DocumentMoveIntentSyncStatus =\n | \"pending\"\n | \"blocked\"\n | \"denied\"\n | \"unavailable\";\n\nexport interface DocumentMoveIntentRecord {\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n id: string;\n documentId: string;\n intentType:\n | typeof DOCUMENT_MOVE_INTENT_TYPE\n | typeof DOCUMENT_LINK_INTENT_TYPE;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: DocumentMoveIntentSyncStatus;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface SelectedDocumentMoveIntentRecord {\n id: string | null;\n documentId: string;\n intentType: string;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: string;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nfunction parseDocumentMoveIntentSyncStatus(\n value: unknown,\n): DocumentMoveIntentSyncStatus {\n if (value === \"blocked\" || value === \"denied\" || value === \"unavailable\") {\n return value;\n }\n return \"pending\";\n}\n\nfunction resolveRecordedSyncStatus(input: {\n blocked?: boolean | undefined;\n denied?: boolean | undefined;\n unavailable?: boolean | undefined;\n}): DocumentMoveIntentSyncStatus {\n if (input.unavailable) return \"unavailable\";\n if (input.denied) return \"denied\";\n return input.blocked ? \"blocked\" : \"pending\";\n}\n\nfunction mapDocumentMoveIntentRecord(\n row: SelectedDocumentMoveIntentRecord,\n): DocumentMoveIntentRecord {\n return {\n id: String(row.id ?? \"\"),\n documentId: row.documentId,\n intentType:\n row.intentType === DOCUMENT_LINK_INTENT_TYPE\n ? DOCUMENT_LINK_INTENT_TYPE\n : DOCUMENT_MOVE_INTENT_TYPE,\n lastAttemptedAt: row.lastAttemptedAt,\n lastError: row.lastError,\n localId: row.localId,\n replaceLinkedContainers: Boolean(row.replaceLinkedContainers),\n sourceContainerId: row.sourceContainerId,\n syncStatus: parseDocumentMoveIntentSyncStatus(row.syncStatus),\n targetContainerId: row.targetContainerId,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport const sqlDocumentMoveIntentPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, documentMoveIntentTables);\n },\n\n enqueueMoveIntent: enqueueDocumentMoveIntent,\n enqueueLinkIntent: enqueueDocumentLinkIntent,\n enqueueUnlinkIntent: enqueueDocumentUnlinkIntent,\n\n async listPendingMoveIntents(\n execSql: ExecSql,\n ): Promise<DocumentMoveIntentRecord[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const rows = await tx\n .select()\n .from(documentMoveIntents)\n // Blocked intents replay too: \"blocked\" names the reason the last\n // attempt could not proceed (missing local doc / destination), not a\n // terminal verdict. The blocking condition can heal after hydration or\n // recovery, and re-checking is cheap — a still-blocked intent simply\n // re-records its reason without counting as lane progress.\n // Unavailable intents never replay: the server proved a cited container\n // is gone, so every replay would re-issue the same doomed requests\n // (#2278 #4). Only the tombstone cascade or a re-enqueue revives them.\n .where(\n and(\n inArray(documentMoveIntents.syncStatus, [\"pending\", \"blocked\"]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ),\n )\n .orderBy(asc(documentMoveIntents.createdAt));\n\n return Promise.all(\n rows.map(async (row) => {\n const targets = await loadDocumentIntentLinkTargets(\n lockedExecSql,\n row.id ?? \"\",\n );\n const added = targets\n .filter((target) => target.operation === \"link\")\n .map((target) => target.containerId)\n .sort();\n const removed = targets\n .filter((target) => target.operation === \"unlink\")\n .map((target) => target.containerId)\n .sort();\n return {\n ...mapDocumentMoveIntentRecord(row),\n ...(added.length ? { additionalLinkContainerIds: added } : {}),\n ...(removed.length ? { removedLinkContainerIds: removed } : {}),\n };\n }),\n );\n },\n );\n });\n },\n\n async markMoveIntentSynced(\n execSql: ExecSql,\n input: {\n documentId: string;\n expectedIntentId?: string | undefined;\n expectedUpdatedAt: string;\n },\n ): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const deleted = await lockedExecSql(\n `DELETE FROM document_move_intents\n WHERE document_id = ?\n AND intent_type IN (?, ?)\n AND updated_at = ?\n ${input.expectedIntentId ? \"AND id = ?\" : \"\"}\n RETURNING id`,\n [\n input.documentId,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n input.expectedUpdatedAt,\n ...(input.expectedIntentId ? [input.expectedIntentId] : []),\n ],\n );\n for (const { id } of deleted) {\n await getClientSQLitePersistenceRuntime(lockedExecSql)\n .db.delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, String(id)))\n .run();\n }\n return deleted.length > 0;\n });\n },\n\n async recordMoveIntentError(\n execSql: ExecSql,\n input: {\n blocked?: boolean | undefined;\n /**\n * A permission denial (403): the intent parks as `denied` — excluded\n * from routine structural replays — until the org-access-restored\n * signal or a manual retry flips it back to pending (edge-case row 7).\n */\n denied?: boolean | undefined;\n documentId: string;\n /** Exact intent revision read by the pass; preferred over wall-clock CAS. */\n expectedIntentId?: string | undefined;\n /**\n * Optimistic concurrency: when given, the error only records against\n * the intent revision the pass read — a re-enqueued move (new\n * updatedAt) is never parked by a stale in-flight failure.\n */\n expectedUpdatedAt?: string | undefined;\n message: string;\n stillCurrent?: (() => boolean) | undefined;\n /**\n * Server proof that a cited container was deleted (coded 404 or\n * `container_unavailable` 409): the intent parks terminally as\n * `unavailable`, outside every replay, until the tombstone cascade\n * retargets it or a re-enqueue replaces it. Outranks `denied`: a\n * restored permission cannot revive a deleted container.\n */\n unavailable?: boolean | undefined;\n },\n ): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).guardedTransaction(\n async (db) => {\n const updatedAt = new Date().toISOString();\n await db\n .update(documentMoveIntents)\n .set({\n lastAttemptedAt: updatedAt,\n lastError: input.message,\n syncStatus: resolveRecordedSyncStatus(input),\n updatedAt,\n })\n .where(\n and(\n eq(documentMoveIntents.documentId, input.documentId),\n // Blocked/denied rows must stay updatable: a retried intent\n // records its fresh outcome, and a transient failure flips it\n // back to pending instead of freezing it forever.\n inArray(documentMoveIntents.syncStatus, [\n \"pending\",\n \"blocked\",\n \"denied\",\n ]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ...(input.expectedIntentId\n ? [eq(documentMoveIntents.id, input.expectedIntentId)]\n : []),\n ...(input.expectedUpdatedAt\n ? [eq(documentMoveIntents.updatedAt, input.expectedUpdatedAt)]\n : []),\n ),\n )\n .run();\n },\n input.stillCurrent ?? (() => true),\n { behavior: \"immediate\" },\n );\n },\n /**\n * Evidence for the org-access-restored re-arm gate: a parked\n * permission-denied move proves a queued write was refused.\n */\n async hasDeniedMoveIntents(\n execSql: ExecSql,\n input?: { organizationId?: string },\n ): Promise<boolean> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n const rows = await execSql(\n `SELECT intent.document_id AS document_id\n FROM document_move_intents intent\n ${input?.organizationId ? DENIED_INTENT_ORGANIZATION_JOINS_SQL : \"\"}\n WHERE intent.sync_status = 'denied'\n AND intent.intent_type IN (?, ?)\n ${input?.organizationId ? `AND (${DENIED_INTENT_ORGANIZATION_SQL} = ? OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL)` : \"\"}\n LIMIT 1`,\n input?.organizationId\n ? [...DOCUMENT_PLACEMENT_INTENT_TYPES, input.organizationId]\n : DOCUMENT_PLACEMENT_INTENT_TYPES,\n );\n return rows.length > 0;\n },\n /**\n * Flip parked permission-denied moves back to pending so the re-armed\n * structural pass replays them (access restored, or a manual retry scoped\n * to one document).\n */\n async resetDeniedMoveIntents(\n execSql: ExecSql,\n input?: { localId?: string; organizationId?: string },\n ): Promise<void> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // Organization scoping resolves each intent's organization through the\n // container joins above: restoring one organization's access must not\n // un-park moves that another organization still denies. Rows with no\n // resolvable organization are included (device-first shapes).\n await lockedExecSql(\n `UPDATE document_move_intents\n SET sync_status = 'pending', updated_at = ?\n WHERE sync_status = 'denied'\n AND intent_type IN (?, ?)\n ${input?.localId ? \"AND local_id = ?\" : \"\"}\n ${\n input?.organizationId\n ? `AND local_id IN (\n SELECT intent.local_id FROM document_move_intents intent\n ${DENIED_INTENT_ORGANIZATION_JOINS_SQL}\n WHERE ${DENIED_INTENT_ORGANIZATION_SQL} = ?\n OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL\n )`\n : \"\"\n}`,\n [\n new Date().toISOString(),\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n ...(input?.localId ? [input.localId] : []),\n ...(input?.organizationId ? [input.organizationId] : []),\n ],\n );\n });\n },\n};\n"]}
1
+ {"version":3,"file":"documentMoveIntentPersistence.js","sourceRoot":"","sources":["documentMoveIntentPersistence.ts"],"names":[],"mappings":"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","sourcesContent":["import { and, asc, eq, inArray } from \"drizzle-orm\";\nimport { documentIntentLinkTargets } from \"../../sqlite/documentPlacementIntentSchema\";\nimport {\n containerTables,\n documentMoveIntents,\n documentMoveIntentTables,\n documentProjectionTables,\n} from \"../../sqlite/schema\";\nimport { getClientSQLitePersistenceRuntime } from \"../../sqlite/sqlitePersistenceRuntime\";\nimport {\n type ExecSql,\n ensureSqlTables,\n runSerializedSqlMutation,\n} from \"../../sqlite/sqlSchema\";\nimport {\n enqueueDocumentLinkIntent,\n enqueueDocumentMoveIntent,\n enqueueDocumentUnlinkIntent,\n loadDocumentIntentLinkTargets,\n} from \"./documentPlacementIntentEnqueue\";\n\nexport const DOCUMENT_MOVE_INTENT_TYPE = \"document.move\";\nexport const DOCUMENT_LINK_INTENT_TYPE = \"document.link\";\nconst DOCUMENT_PLACEMENT_INTENT_TYPES = [\n DOCUMENT_MOVE_INTENT_TYPE,\n DOCUMENT_LINK_INTENT_TYPE,\n];\n\n// Resolves the organization a parked move belongs to, in confidence order:\n// the move's target container, the document's preserved projection\n// attribution, then the container the document currently sits in. Normal\n// saves routinely leave projection attribution empty (deferred-tail\n// reporting resolves through the same container join), so the container rows\n// carry the common case; rows with no resolvable organization stay in every\n// scope (device-first shapes, matching orphan priming).\nconst DENIED_INTENT_ORGANIZATION_SQL = `COALESCE(\n NULLIF(target_container.organization_id, ''),\n NULLIF(projection.organization_id, ''),\n NULLIF(projection_container.organization_id, '')\n)`;\n\nconst DENIED_INTENT_ORGANIZATION_JOINS_SQL = `\n LEFT JOIN containers target_container\n ON target_container.id = intent.target_container_id\n LEFT JOIN document_projection projection\n ON projection.local_id = intent.local_id\n LEFT JOIN containers projection_container\n ON projection_container.id = projection.container_id`;\n\n/**\n * - `pending`: replays on every structural pass.\n * - `blocked`: the last pass could not proceed locally (missing local\n * document / destination); replays, since hydration can heal it.\n * - `denied`: parked on a 403 until the access-restored replay (row 7).\n * - `unavailable`: terminal — after a projection refresh the destination\n * itself is proven deleted (coded 404 on its own projection probe). No\n * mutation 409 is ever terminal: with the destination live it can only\n * name an ancestor or source, and the next pass's fresh paths outrun it.\n * Container ids never return, so no replay can commit the intent as\n * written; it leaves the replay set until the local container tombstone\n * cascade retargets it or the user re-enqueues the move.\n */\nexport type DocumentMoveIntentSyncStatus =\n | \"pending\"\n | \"blocked\"\n | \"denied\"\n | \"unavailable\";\n\nexport interface DocumentMoveIntentRecord {\n additionalLinkContainerIds?: readonly string[] | undefined;\n removedLinkContainerIds?: readonly string[] | undefined;\n id: string;\n documentId: string;\n intentType:\n | typeof DOCUMENT_MOVE_INTENT_TYPE\n | typeof DOCUMENT_LINK_INTENT_TYPE;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: DocumentMoveIntentSyncStatus;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface SelectedDocumentMoveIntentRecord {\n id: string | null;\n documentId: string;\n intentType: string;\n lastAttemptedAt: string | null;\n lastError: string | null;\n localId: string;\n replaceLinkedContainers: boolean;\n sourceContainerId: string | null;\n syncStatus: string;\n targetContainerId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nfunction parseDocumentMoveIntentSyncStatus(\n value: unknown,\n): DocumentMoveIntentSyncStatus {\n if (value === \"blocked\" || value === \"denied\" || value === \"unavailable\") {\n return value;\n }\n return \"pending\";\n}\n\nfunction resolveRecordedSyncStatus(input: {\n blocked?: boolean | undefined;\n denied?: boolean | undefined;\n unavailable?: boolean | undefined;\n}): DocumentMoveIntentSyncStatus {\n if (input.unavailable) return \"unavailable\";\n if (input.denied) return \"denied\";\n return input.blocked ? \"blocked\" : \"pending\";\n}\n\nfunction mapDocumentMoveIntentRecord(\n row: SelectedDocumentMoveIntentRecord,\n): DocumentMoveIntentRecord {\n return {\n id: String(row.id ?? \"\"),\n documentId: row.documentId,\n intentType:\n row.intentType === DOCUMENT_LINK_INTENT_TYPE\n ? DOCUMENT_LINK_INTENT_TYPE\n : DOCUMENT_MOVE_INTENT_TYPE,\n lastAttemptedAt: row.lastAttemptedAt,\n lastError: row.lastError,\n localId: row.localId,\n replaceLinkedContainers: Boolean(row.replaceLinkedContainers),\n sourceContainerId: row.sourceContainerId,\n syncStatus: parseDocumentMoveIntentSyncStatus(row.syncStatus),\n targetContainerId: row.targetContainerId,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nexport const sqlDocumentMoveIntentPersistence = {\n async ensureSchema(execSql: ExecSql): Promise<void> {\n await ensureSqlTables(execSql, documentMoveIntentTables);\n },\n\n enqueueMoveIntent: enqueueDocumentMoveIntent,\n enqueueLinkIntent: enqueueDocumentLinkIntent,\n enqueueUnlinkIntent: enqueueDocumentUnlinkIntent,\n\n async listPendingMoveIntents(\n execSql: ExecSql,\n ): Promise<DocumentMoveIntentRecord[]> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n await ensureSqlTables(lockedExecSql, documentMoveIntentTables);\n return getClientSQLitePersistenceRuntime(lockedExecSql).transaction(\n async (tx) => {\n const rows = await tx\n .select()\n .from(documentMoveIntents)\n // Blocked intents replay too: \"blocked\" names the reason the last\n // attempt could not proceed (missing local doc / destination), not a\n // terminal verdict. The blocking condition can heal after hydration or\n // recovery, and re-checking is cheap — a still-blocked intent simply\n // re-records its reason without counting as lane progress.\n // Unavailable intents never replay: the server proved a cited container\n // is gone, so every replay would re-issue the same doomed requests\n // (#2278 #4). Only the tombstone cascade or a re-enqueue revives them.\n .where(\n and(\n inArray(documentMoveIntents.syncStatus, [\"pending\", \"blocked\"]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ),\n )\n .orderBy(asc(documentMoveIntents.createdAt));\n\n return Promise.all(\n rows.map(async (row) => {\n const targets = await loadDocumentIntentLinkTargets(\n lockedExecSql,\n row.id ?? \"\",\n );\n const added = targets\n .filter((target) => target.operation === \"link\")\n .map((target) => target.containerId)\n .sort();\n const removed = targets\n .filter((target) => target.operation === \"unlink\")\n .map((target) => target.containerId)\n .sort();\n return {\n ...mapDocumentMoveIntentRecord(row),\n ...(added.length ? { additionalLinkContainerIds: added } : {}),\n ...(removed.length ? { removedLinkContainerIds: removed } : {}),\n };\n }),\n );\n },\n );\n });\n },\n\n async markMoveIntentSynced(\n execSql: ExecSql,\n input: {\n documentId: string;\n expectedIntentId?: string | undefined;\n expectedUpdatedAt: string;\n },\n ): Promise<boolean> {\n return runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n const deleted = await lockedExecSql(\n `DELETE FROM document_move_intents\n WHERE document_id = ?\n AND intent_type IN (?, ?)\n AND updated_at = ?\n ${input.expectedIntentId ? \"AND id = ?\" : \"\"}\n RETURNING id`,\n [\n input.documentId,\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n input.expectedUpdatedAt,\n ...(input.expectedIntentId ? [input.expectedIntentId] : []),\n ],\n );\n for (const { id } of deleted) {\n await getClientSQLitePersistenceRuntime(lockedExecSql)\n .db.delete(documentIntentLinkTargets)\n .where(eq(documentIntentLinkTargets.intentId, String(id)))\n .run();\n }\n return deleted.length > 0;\n });\n },\n\n async recordMoveIntentError(\n execSql: ExecSql,\n input: {\n blocked?: boolean | undefined;\n /**\n * A permission denial (403): the intent parks as `denied` — excluded\n * from routine structural replays — until the org-access-restored\n * signal or a manual retry flips it back to pending (edge-case row 7).\n */\n denied?: boolean | undefined;\n documentId: string;\n /** Exact intent revision read by the pass; preferred over wall-clock CAS. */\n expectedIntentId?: string | undefined;\n /**\n * Optimistic concurrency: when given, the error only records against\n * the intent revision the pass read — a re-enqueued move (new\n * updatedAt) is never parked by a stale in-flight failure.\n */\n expectedUpdatedAt?: string | undefined;\n message: string;\n stillCurrent?: (() => boolean) | undefined;\n /**\n * Server proof that a cited container was deleted (coded 404 or\n * `container_unavailable` 409): the intent parks terminally as\n * `unavailable`, outside every replay, until the tombstone cascade\n * retargets it or a re-enqueue replaces it. Outranks `denied`: a\n * restored permission cannot revive a deleted container.\n */\n unavailable?: boolean | undefined;\n },\n ): Promise<void> {\n await getClientSQLitePersistenceRuntime(execSql).guardedTransaction(\n async (db) => {\n const updatedAt = new Date().toISOString();\n await db\n .update(documentMoveIntents)\n .set({\n lastAttemptedAt: updatedAt,\n lastError: input.message,\n syncStatus: resolveRecordedSyncStatus(input),\n updatedAt,\n })\n .where(\n and(\n eq(documentMoveIntents.documentId, input.documentId),\n // Blocked/denied rows must stay updatable: a retried intent\n // records its fresh outcome, and a transient failure flips it\n // back to pending instead of freezing it forever.\n inArray(documentMoveIntents.syncStatus, [\n \"pending\",\n \"blocked\",\n \"denied\",\n ]),\n inArray(\n documentMoveIntents.intentType,\n DOCUMENT_PLACEMENT_INTENT_TYPES,\n ),\n ...(input.expectedIntentId\n ? [eq(documentMoveIntents.id, input.expectedIntentId)]\n : []),\n ...(input.expectedUpdatedAt\n ? [eq(documentMoveIntents.updatedAt, input.expectedUpdatedAt)]\n : []),\n ),\n )\n .run();\n },\n input.stillCurrent ?? (() => true),\n { behavior: \"immediate\" },\n );\n },\n /**\n * Evidence for the org-access-restored re-arm gate: a parked\n * permission-denied move proves a queued write was refused.\n */\n async hasDeniedMoveIntents(\n execSql: ExecSql,\n input?: { organizationId?: string },\n ): Promise<boolean> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n const rows = await execSql(\n `SELECT intent.document_id AS document_id\n FROM document_move_intents intent\n ${input?.organizationId ? DENIED_INTENT_ORGANIZATION_JOINS_SQL : \"\"}\n WHERE intent.sync_status = 'denied'\n AND intent.intent_type IN (?, ?)\n ${input?.organizationId ? `AND (${DENIED_INTENT_ORGANIZATION_SQL} = ? OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL)` : \"\"}\n LIMIT 1`,\n input?.organizationId\n ? [...DOCUMENT_PLACEMENT_INTENT_TYPES, input.organizationId]\n : DOCUMENT_PLACEMENT_INTENT_TYPES,\n );\n return rows.length > 0;\n },\n /**\n * Flip parked permission-denied moves back to pending so the re-armed\n * structural pass replays them (access restored, or a manual retry scoped\n * to one document).\n */\n async resetDeniedMoveIntents(\n execSql: ExecSql,\n input?: { localId?: string; organizationId?: string },\n ): Promise<void> {\n await sqlDocumentMoveIntentPersistence.ensureSchema(execSql);\n await ensureSqlTables(execSql, [\n ...documentProjectionTables,\n ...containerTables,\n ]);\n await runSerializedSqlMutation(execSql, async (lockedExecSql) => {\n // Organization scoping resolves each intent's organization through the\n // container joins above: restoring one organization's access must not\n // un-park moves that another organization still denies. Rows with no\n // resolvable organization are included (device-first shapes).\n await lockedExecSql(\n `UPDATE document_move_intents\n SET sync_status = 'pending', updated_at = ?\n WHERE sync_status = 'denied'\n AND intent_type IN (?, ?)\n ${input?.localId ? \"AND local_id = ?\" : \"\"}\n ${\n input?.organizationId\n ? `AND local_id IN (\n SELECT intent.local_id FROM document_move_intents intent\n ${DENIED_INTENT_ORGANIZATION_JOINS_SQL}\n WHERE ${DENIED_INTENT_ORGANIZATION_SQL} = ?\n OR ${DENIED_INTENT_ORGANIZATION_SQL} IS NULL\n )`\n : \"\"\n }`,\n [\n new Date().toISOString(),\n ...DOCUMENT_PLACEMENT_INTENT_TYPES,\n ...(input?.localId ? [input.localId] : []),\n ...(input?.organizationId ? [input.organizationId] : []),\n ],\n );\n });\n },\n};\n"]}
@@ -1,16 +1,13 @@
1
1
  import { type SigningKeyPair } from "../../internal/crypto/index.js";
2
- import { type CommitOrganizationGroupPolicyRequest } from "../../internal/validators/request/index.js";
3
2
  import type { PrincipalMutationJournalRow } from "../persistence/principalMutationJournalPersistence.js";
3
+ import { type AuthoredPrincipalMutation } from "./principalMutationJournalShape.js";
4
4
  export interface PrincipalMutationJournalScope {
5
5
  readonly identityTrustDomain: string;
6
6
  readonly organizationId: string;
7
7
  readonly userId: string;
8
8
  readonly signingFingerprint: string;
9
9
  }
10
- export interface AuthoredPrincipalMutation {
11
- readonly groupId: string;
12
- readonly request: CommitOrganizationGroupPolicyRequest;
13
- }
10
+ export type { AuthoredPrincipalMutation } from "./principalMutationJournalShape.js";
14
11
  export declare function principalMutationJournalScopeId(scope: PrincipalMutationJournalScope): Promise<string>;
15
12
  /** Sign the complete wire body, including artifacts outside policy signatures. */
16
13
  export declare function sealPrincipalMutation(input: {
@@ -1,7 +1,7 @@
1
1
  import { KeyingVerificationError, sign, toFingerprint, verify, } from "../../internal/crypto/index.js";
2
2
  import { base64ToBytes, bytesToBase64 } from "../../internal/encoding/index.js";
3
- import { CommitOrganizationGroupPolicyRequestSchema, } from "../../internal/validators/request/index.js";
4
3
  import { canonicalKeyingJsonString } from "../keyingCanonicalJson.js";
4
+ import { readPrincipalMutation, } from "./principalMutationJournalShape.js";
5
5
  const journalDomain = "tearleads.sdk.principal-mutation.v1";
6
6
  const encoder = new TextEncoder();
7
7
  function scopeJson(scope) {
@@ -18,23 +18,6 @@ function scopeJson(scope) {
18
18
  export async function principalMutationJournalScopeId(scope) {
19
19
  return bytesToBase64(new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(scopeJson(scope)))));
20
20
  }
21
- function readMutation(value, scope) {
22
- if (typeof value !== "object" || value === null)
23
- throw new Error("Principal mutation journal payload is invalid");
24
- const groupId = Reflect.get(value, "groupId");
25
- const request = CommitOrganizationGroupPolicyRequestSchema.parse(Reflect.get(value, "request"));
26
- const group = request.groupPolicy.state;
27
- const organization = request.organizationPolicy.state;
28
- if (typeof groupId !== "string" ||
29
- group.principalType !== "group" ||
30
- group.principalId !== groupId ||
31
- organization.principalType !== "organization" ||
32
- organization.principalId !== scope.organizationId ||
33
- [group, organization].some((state) => state.signerUserId !== scope.userId ||
34
- state.signerUserKeyFingerprint !== scope.signingFingerprint))
35
- throw new Error("Principal mutation journal target or signer differs");
36
- return { groupId, request };
37
- }
38
21
  function signedBytes(scope, request) {
39
22
  // The domain-tagged scope and JSON tuple separate these identity-key
40
23
  // signatures from policy state signatures and other authored formats.
@@ -43,7 +26,7 @@ function signedBytes(scope, request) {
43
26
  /** Sign the complete wire body, including artifacts outside policy signatures. */
44
27
  export async function sealPrincipalMutation(input) {
45
28
  const scope = { ...input.scope };
46
- const mutation = readMutation(JSON.parse(JSON.stringify(input.mutation)), scope);
29
+ const mutation = readPrincipalMutation(JSON.parse(JSON.stringify(input.mutation)), scope);
47
30
  const serializedRequest = canonicalKeyingJsonString(mutation, "authored principal mutation");
48
31
  const publicKey = new Uint8Array(input.signingKeyPair.signingPublicKey);
49
32
  const privateKey = new Uint8Array(input.signingKeyPair.signingPrivateKey);
@@ -81,7 +64,7 @@ export async function openPrincipalMutation(input) {
81
64
  throw new KeyingVerificationError("signature_mismatch", "Saved principal mutation could not be authenticated");
82
65
  }
83
66
  try {
84
- return readMutation(JSON.parse(row.serializedRequest), scope);
67
+ return readPrincipalMutation(JSON.parse(row.serializedRequest), scope);
85
68
  }
86
69
  catch {
87
70
  throw new KeyingVerificationError("invalid_shape", "Authenticated principal mutation has an unsupported or invalid format");
@@ -1 +1 @@
1
- {"version":3,"file":"principalMutationJournal.js","sourceRoot":"","sources":["principalMutationJournal.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type SigningKeyPair,\n sign,\n toFingerprint,\n verify,\n} from \"@tearleads/crypto\";\nimport { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport {\n type CommitOrganizationGroupPolicyRequest,\n CommitOrganizationGroupPolicyRequestSchema,\n} from \"@tearleads/validators/request\";\nimport { canonicalKeyingJsonString } from \"../keyingCanonicalJson\";\nimport type { PrincipalMutationJournalRow } from \"../persistence/principalMutationJournalPersistence\";\n\nexport interface PrincipalMutationJournalScope {\n readonly identityTrustDomain: string;\n readonly organizationId: string;\n readonly userId: string;\n readonly signingFingerprint: string;\n}\n\nexport interface AuthoredPrincipalMutation {\n readonly groupId: string;\n readonly request: CommitOrganizationGroupPolicyRequest;\n}\n\nconst journalDomain = \"tearleads.sdk.principal-mutation.v1\";\nconst encoder = new TextEncoder();\n\nfunction scopeJson(scope: PrincipalMutationJournalScope): string {\n const values = [\n scope.identityTrustDomain,\n scope.organizationId,\n scope.userId,\n scope.signingFingerprint,\n ];\n if (values.some((value) => typeof value !== \"string\" || value.length === 0))\n throw new Error(\"Principal mutation journal requires a complete scope\");\n return JSON.stringify([journalDomain, ...values]);\n}\n\nexport async function principalMutationJournalScopeId(\n scope: PrincipalMutationJournalScope,\n): Promise<string> {\n return bytesToBase64(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", encoder.encode(scopeJson(scope))),\n ),\n );\n}\n\nfunction readMutation(\n value: unknown,\n scope: PrincipalMutationJournalScope,\n): AuthoredPrincipalMutation {\n if (typeof value !== \"object\" || value === null)\n throw new Error(\"Principal mutation journal payload is invalid\");\n const groupId: unknown = Reflect.get(value, \"groupId\");\n const request = CommitOrganizationGroupPolicyRequestSchema.parse(\n Reflect.get(value, \"request\"),\n );\n const group = request.groupPolicy.state;\n const organization = request.organizationPolicy.state;\n if (\n typeof groupId !== \"string\" ||\n group.principalType !== \"group\" ||\n group.principalId !== groupId ||\n organization.principalType !== \"organization\" ||\n organization.principalId !== scope.organizationId ||\n [group, organization].some(\n (state) =>\n state.signerUserId !== scope.userId ||\n state.signerUserKeyFingerprint !== scope.signingFingerprint,\n )\n )\n throw new Error(\"Principal mutation journal target or signer differs\");\n return { groupId, request };\n}\n\nfunction signedBytes(scope: PrincipalMutationJournalScope, request: string) {\n // The domain-tagged scope and JSON tuple separate these identity-key\n // signatures from policy state signatures and other authored formats.\n return encoder.encode(JSON.stringify([scopeJson(scope), request]));\n}\n\n/** Sign the complete wire body, including artifacts outside policy signatures. */\nexport async function sealPrincipalMutation(input: {\n readonly scope: PrincipalMutationJournalScope;\n readonly mutation: AuthoredPrincipalMutation;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<PrincipalMutationJournalRow> {\n const scope = { ...input.scope };\n const mutation = readMutation(\n JSON.parse(JSON.stringify(input.mutation)),\n scope,\n );\n const serializedRequest = canonicalKeyingJsonString(\n mutation,\n \"authored principal mutation\",\n );\n const publicKey = new Uint8Array(input.signingKeyPair.signingPublicKey);\n const privateKey = new Uint8Array(input.signingKeyPair.signingPrivateKey);\n try {\n if ((await toFingerprint(publicKey)) !== scope.signingFingerprint)\n throw new Error(\"Principal mutation journal signing key differs\");\n const bytes = signedBytes(scope, serializedRequest);\n const signature = sign(bytes, privateKey);\n if (!verify(signature, bytes, publicKey))\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"Principal mutation journal signing key pair differs\",\n );\n return {\n scopeId: await principalMutationJournalScopeId(scope),\n organizationId: scope.organizationId,\n serializedRequest,\n signature: bytesToBase64(signature),\n };\n } finally {\n privateKey.fill(0);\n }\n}\n\n/** Authentication precedes parsing or submission; corrupt authored work is retained. */\nexport async function openPrincipalMutation(input: {\n readonly scope: PrincipalMutationJournalScope;\n readonly row: PrincipalMutationJournalRow;\n readonly signingPublicKey: Uint8Array;\n}): Promise<AuthoredPrincipalMutation> {\n const scope = { ...input.scope };\n const row = { ...input.row };\n const publicKey = new Uint8Array(input.signingPublicKey);\n try {\n if (\n row.organizationId !== scope.organizationId ||\n row.scopeId !== (await principalMutationJournalScopeId(scope)) ||\n (await toFingerprint(publicKey)) !== scope.signingFingerprint ||\n !verify(\n base64ToBytes(row.signature),\n signedBytes(scope, row.serializedRequest),\n publicKey,\n )\n )\n throw new Error(\"Principal mutation journal authentication failed\");\n } catch {\n throw new KeyingVerificationError(\n \"signature_mismatch\",\n \"Saved principal mutation could not be authenticated\",\n );\n }\n try {\n return readMutation(JSON.parse(row.serializedRequest), scope);\n } catch {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Authenticated principal mutation has an unsupported or invalid format\",\n );\n }\n}\n"]}
1
+ {"version":3,"file":"principalMutationJournal.js","sourceRoot":"","sources":["principalMutationJournal.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type SigningKeyPair,\n sign,\n toFingerprint,\n verify,\n} from \"@tearleads/crypto\";\nimport { base64ToBytes, bytesToBase64 } from \"@tearleads/encoding\";\nimport { canonicalKeyingJsonString } from \"../keyingCanonicalJson\";\nimport type { PrincipalMutationJournalRow } from \"../persistence/principalMutationJournalPersistence\";\nimport {\n type AuthoredPrincipalMutation,\n readPrincipalMutation,\n} from \"./principalMutationJournalShape\";\n\nexport interface PrincipalMutationJournalScope {\n readonly identityTrustDomain: string;\n readonly organizationId: string;\n readonly userId: string;\n readonly signingFingerprint: string;\n}\n\nexport type { AuthoredPrincipalMutation } from \"./principalMutationJournalShape\";\n\nconst journalDomain = \"tearleads.sdk.principal-mutation.v1\";\nconst encoder = new TextEncoder();\n\nfunction scopeJson(scope: PrincipalMutationJournalScope): string {\n const values = [\n scope.identityTrustDomain,\n scope.organizationId,\n scope.userId,\n scope.signingFingerprint,\n ];\n if (values.some((value) => typeof value !== \"string\" || value.length === 0))\n throw new Error(\"Principal mutation journal requires a complete scope\");\n return JSON.stringify([journalDomain, ...values]);\n}\n\nexport async function principalMutationJournalScopeId(\n scope: PrincipalMutationJournalScope,\n): Promise<string> {\n return bytesToBase64(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", encoder.encode(scopeJson(scope))),\n ),\n );\n}\n\nfunction signedBytes(scope: PrincipalMutationJournalScope, request: string) {\n // The domain-tagged scope and JSON tuple separate these identity-key\n // signatures from policy state signatures and other authored formats.\n return encoder.encode(JSON.stringify([scopeJson(scope), request]));\n}\n\n/** Sign the complete wire body, including artifacts outside policy signatures. */\nexport async function sealPrincipalMutation(input: {\n readonly scope: PrincipalMutationJournalScope;\n readonly mutation: AuthoredPrincipalMutation;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<PrincipalMutationJournalRow> {\n const scope = { ...input.scope };\n const mutation = readPrincipalMutation(\n JSON.parse(JSON.stringify(input.mutation)),\n scope,\n );\n const serializedRequest = canonicalKeyingJsonString(\n mutation,\n \"authored principal mutation\",\n );\n const publicKey = new Uint8Array(input.signingKeyPair.signingPublicKey);\n const privateKey = new Uint8Array(input.signingKeyPair.signingPrivateKey);\n try {\n if ((await toFingerprint(publicKey)) !== scope.signingFingerprint)\n throw new Error(\"Principal mutation journal signing key differs\");\n const bytes = signedBytes(scope, serializedRequest);\n const signature = sign(bytes, privateKey);\n if (!verify(signature, bytes, publicKey))\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"Principal mutation journal signing key pair differs\",\n );\n return {\n scopeId: await principalMutationJournalScopeId(scope),\n organizationId: scope.organizationId,\n serializedRequest,\n signature: bytesToBase64(signature),\n };\n } finally {\n privateKey.fill(0);\n }\n}\n\n/** Authentication precedes parsing or submission; corrupt authored work is retained. */\nexport async function openPrincipalMutation(input: {\n readonly scope: PrincipalMutationJournalScope;\n readonly row: PrincipalMutationJournalRow;\n readonly signingPublicKey: Uint8Array;\n}): Promise<AuthoredPrincipalMutation> {\n const scope = { ...input.scope };\n const row = { ...input.row };\n const publicKey = new Uint8Array(input.signingPublicKey);\n try {\n if (\n row.organizationId !== scope.organizationId ||\n row.scopeId !== (await principalMutationJournalScopeId(scope)) ||\n (await toFingerprint(publicKey)) !== scope.signingFingerprint ||\n !verify(\n base64ToBytes(row.signature),\n signedBytes(scope, row.serializedRequest),\n publicKey,\n )\n )\n throw new Error(\"Principal mutation journal authentication failed\");\n } catch {\n throw new KeyingVerificationError(\n \"signature_mismatch\",\n \"Saved principal mutation could not be authenticated\",\n );\n }\n try {\n return readPrincipalMutation(JSON.parse(row.serializedRequest), scope);\n } catch {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"Authenticated principal mutation has an unsupported or invalid format\",\n );\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { type CommitOrganizationGroupPolicyRequest, type CreateOrganizationGroupWithPolicyRequest, type DeleteOrganizationGroupRequest, type OrganizationPrincipalPolicyRequest } from "../../internal/validators/request/index.js";
2
+ export type AuthoredPrincipalMutation = {
3
+ readonly kind?: undefined;
4
+ readonly groupId: string;
5
+ readonly request: CommitOrganizationGroupPolicyRequest;
6
+ } | {
7
+ readonly kind: "group-create";
8
+ readonly groupId: string;
9
+ readonly request: CreateOrganizationGroupWithPolicyRequest;
10
+ } | {
11
+ readonly kind: "group-delete";
12
+ readonly groupId: string;
13
+ readonly request: DeleteOrganizationGroupRequest;
14
+ } | {
15
+ readonly kind: "organization";
16
+ readonly groupId: null;
17
+ readonly request: OrganizationPrincipalPolicyRequest;
18
+ };
19
+ interface SignerScope {
20
+ readonly organizationId: string;
21
+ readonly userId: string;
22
+ readonly signingFingerprint: string;
23
+ }
24
+ /** Validate authored inputs; opening an existing row authenticates it first. */
25
+ export declare function readPrincipalMutation(value: unknown, scope: SignerScope): AuthoredPrincipalMutation;
26
+ export {};
@@ -0,0 +1,46 @@
1
+ import { CommitOrganizationGroupPolicyRequestSchema, CreateOrganizationGroupWithPolicyRequestSchema, DeleteOrganizationGroupRequestSchema, OrganizationPrincipalPolicyRequestSchema, } from "../../internal/validators/request/index.js";
2
+ import { isUuidV4String } from "../../internal/validators/util/index.js";
3
+ /** Validate authored inputs; opening an existing row authenticates it first. */
4
+ export function readPrincipalMutation(value, scope) {
5
+ if (typeof value !== "object" || value === null)
6
+ throw new Error("Principal mutation journal payload is invalid");
7
+ const groupId = Reflect.get(value, "groupId");
8
+ const kind = Reflect.get(value, "kind");
9
+ const body = Reflect.get(value, "request");
10
+ if (kind === "organization") {
11
+ if (groupId !== null)
12
+ throw new Error("Organization journal target differs");
13
+ const request = OrganizationPrincipalPolicyRequestSchema.parse(body);
14
+ assertStateScope(request.state, "organization", scope.organizationId, scope);
15
+ return { kind, groupId, request };
16
+ }
17
+ if (typeof groupId !== "string" || !isUuidV4String(groupId))
18
+ throw new Error("Principal mutation journal group target is invalid");
19
+ if (kind === "group-create") {
20
+ const request = CreateOrganizationGroupWithPolicyRequestSchema.parse(body);
21
+ if (request.groupId !== groupId)
22
+ throw new Error("Group creation journal target differs");
23
+ assertStateScope(request.initialGroupPolicy.state, "group", groupId, scope);
24
+ assertStateScope(request.organizationPolicy.state, "organization", scope.organizationId, scope);
25
+ return { kind, groupId, request };
26
+ }
27
+ if (kind === "group-delete") {
28
+ const request = DeleteOrganizationGroupRequestSchema.parse(body);
29
+ assertStateScope(request.organizationPolicy.state, "organization", scope.organizationId, scope);
30
+ return { kind, groupId, request };
31
+ }
32
+ if (kind !== undefined)
33
+ throw new Error("Principal mutation journal operation is unsupported");
34
+ const request = CommitOrganizationGroupPolicyRequestSchema.parse(body);
35
+ assertStateScope(request.groupPolicy.state, "group", groupId, scope);
36
+ assertStateScope(request.organizationPolicy.state, "organization", scope.organizationId, scope);
37
+ return { groupId, request };
38
+ }
39
+ function assertStateScope(state, principalType, principalId, scope) {
40
+ if (state.principalType !== principalType ||
41
+ state.principalId !== principalId ||
42
+ state.signerUserId !== scope.userId ||
43
+ state.signerUserKeyFingerprint !== scope.signingFingerprint)
44
+ throw new Error("Principal mutation journal target or signer differs");
45
+ }
46
+ //# sourceMappingURL=principalMutationJournalShape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principalMutationJournalShape.js","sourceRoot":"","sources":["principalMutationJournalShape.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type CommitOrganizationGroupPolicyRequest,\n CommitOrganizationGroupPolicyRequestSchema,\n type CreateOrganizationGroupWithPolicyRequest,\n CreateOrganizationGroupWithPolicyRequestSchema,\n type DeleteOrganizationGroupRequest,\n DeleteOrganizationGroupRequestSchema,\n type OrganizationPrincipalPolicyRequest,\n OrganizationPrincipalPolicyRequestSchema,\n} from \"@tearleads/validators/request\";\nimport { isUuidV4String } from \"@tearleads/validators/util\";\n\nexport type AuthoredPrincipalMutation =\n | {\n // Existing signed pending requests have no kind. Keep them in the same\n // scope and recovery lane so an upgrade cannot hide uncertain work.\n readonly kind?: undefined;\n readonly groupId: string;\n readonly request: CommitOrganizationGroupPolicyRequest;\n }\n | {\n readonly kind: \"group-create\";\n readonly groupId: string;\n readonly request: CreateOrganizationGroupWithPolicyRequest;\n }\n | {\n readonly kind: \"group-delete\";\n readonly groupId: string;\n readonly request: DeleteOrganizationGroupRequest;\n }\n | {\n readonly kind: \"organization\";\n readonly groupId: null;\n readonly request: OrganizationPrincipalPolicyRequest;\n };\n\ninterface SignerScope {\n readonly organizationId: string;\n readonly userId: string;\n readonly signingFingerprint: string;\n}\n\n/** Validate authored inputs; opening an existing row authenticates it first. */\nexport function readPrincipalMutation(\n value: unknown,\n scope: SignerScope,\n): AuthoredPrincipalMutation {\n if (typeof value !== \"object\" || value === null)\n throw new Error(\"Principal mutation journal payload is invalid\");\n const groupId: unknown = Reflect.get(value, \"groupId\");\n const kind: unknown = Reflect.get(value, \"kind\");\n const body: unknown = Reflect.get(value, \"request\");\n if (kind === \"organization\") {\n if (groupId !== null)\n throw new Error(\"Organization journal target differs\");\n const request = OrganizationPrincipalPolicyRequestSchema.parse(body);\n assertStateScope(\n request.state,\n \"organization\",\n scope.organizationId,\n scope,\n );\n return { kind, groupId, request };\n }\n if (typeof groupId !== \"string\" || !isUuidV4String(groupId))\n throw new Error(\"Principal mutation journal group target is invalid\");\n if (kind === \"group-create\") {\n const request = CreateOrganizationGroupWithPolicyRequestSchema.parse(body);\n if (request.groupId !== groupId)\n throw new Error(\"Group creation journal target differs\");\n assertStateScope(request.initialGroupPolicy.state, \"group\", groupId, scope);\n assertStateScope(\n request.organizationPolicy.state,\n \"organization\",\n scope.organizationId,\n scope,\n );\n return { kind, groupId, request };\n }\n if (kind === \"group-delete\") {\n const request = DeleteOrganizationGroupRequestSchema.parse(body);\n assertStateScope(\n request.organizationPolicy.state,\n \"organization\",\n scope.organizationId,\n scope,\n );\n return { kind, groupId, request };\n }\n if (kind !== undefined)\n throw new Error(\"Principal mutation journal operation is unsupported\");\n const request = CommitOrganizationGroupPolicyRequestSchema.parse(body);\n assertStateScope(request.groupPolicy.state, \"group\", groupId, scope);\n assertStateScope(\n request.organizationPolicy.state,\n \"organization\",\n scope.organizationId,\n scope,\n );\n return { groupId, request };\n}\n\nfunction assertStateScope(\n state: OrganizationPrincipalPolicyRequest[\"state\"],\n principalType: \"organization\" | \"group\",\n principalId: string,\n scope: SignerScope,\n) {\n if (\n state.principalType !== principalType ||\n state.principalId !== principalId ||\n state.signerUserId !== scope.userId ||\n state.signerUserKeyFingerprint !== scope.signingFingerprint\n )\n throw new Error(\"Principal mutation journal target or signer differs\");\n}\n"]}
package/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export type { AggregatedContainerKekLog, ContainerMutationAuthor, ContainerRecit
14
14
  export { buildMaterializedContainerRekeyPlan, buildRootContainerCreatePlan, ContainerAuthorAccessError, ContainerKekRepairInaccessibleError, ContainerKekRepairRequiredError, ContainerKeyringOverrideStaleError, ContainerPathTooDeepError, classifyContainerWriteRefusal, containerWriterProjectionFromRekeyPlan, createRemoteContainer, fetchContainerKekLog, GroupShareNameMismatchError, HistoricalWrapUnavailableError, type HistoricalWrapUnavailableReason, isProjectionVerificationCancelledError, moveRemoteContainer, rebuildKeyringEntriesFromLog, recoverKeyringEntryFromWraps, rekeyRemoteContainer, revokeRemoteContainer, rootContainerWriterProjectionFromCreatePlan, shareRemoteContainer, shareRemoteContainerWithGroup, } from "./workflows/containers/index.js";
15
15
  export type { DocumentRecord, DocumentSyncContainerRekeyBuilder, DocumentSyncPullContinuation, DocumentSyncUpdateIsolationError, DocumentsPersistence, DocumentsWorkflowRuntime, DocumentsWorkflowRuntimeInput, IncomingDocumentSyncUpdateValidator, LocalAttachmentRecord, PendingAttachmentRecord, PendingUpdateInsert, PendingUpdateRecord, SyncRemoteDocumentInput, } from "./workflows/documents/index.js";
16
16
  export { buildMaterializedDocumentCreatePlan, createDocumentProjectionUserKeyResolver, createDocumentsWorkflowRuntime, createRemoteDocument, DOCUMENT_SYNC_TRACE_FRAGMENT, DOCUMENT_SYNC_TRACE_PATTERN, DOCUMENTS_APP_KIND, DocumentRawHistoryUnavailableError, defaultDocumentsPersistence, deletePersistedDocument, isDatabaseUnavailableError, isDocumentSyncUpdateIsolationError, persistedDocumentCreateStateFromResponse, readPullContinuation, syncRemoteDocument, unwrapContainerKekPath, unwrapDocumentContentKeyTarget, validateDocumentSyncUpdateImports, } from "./workflows/documents/index.js";
17
- export type { AcknowledgedPrincipalCurrentInput, AcknowledgedPrincipalCurrentRetirement, AuthoredPrincipalMutation, OrganizationGroupPolicyHistoryEntry, OrganizationNativePurchaseEligibility, OrganizationPrincipalMemberChange, OrganizationPrincipalMemberChangeType, OrganizationPrincipalPolicyHistory, OrganizationPrincipalPolicyHistoryEntry, PrincipalMutationJournalContext, PrincipalMutationJournalScope, ReconcileOrganizationDataUsageInput, } from "./workflows/organizations/index.js";
17
+ export type { AcknowledgedPrincipalCurrentInput, AcknowledgedPrincipalCurrentRetirement, AuthoredPrincipalMutation, OrganizationGroupPolicyHistoryEntry, OrganizationNativePurchaseEligibility, OrganizationPrincipalMemberChange, OrganizationPrincipalMemberChangeType, OrganizationPrincipalPolicyHistory, OrganizationPrincipalPolicyHistoryEntry, PrincipalMutationJournalContext, PrincipalMutationJournalScope, PrincipalMutationResponse, ReconcileOrganizationDataUsageInput, } from "./workflows/organizations/index.js";
18
18
  export { abandonJournaledPrincipalMutation, addOrganizationGroupUser, buildInitialGroupPolicyRequest, buildInitialMemberGroupPolicyRequest, buildOrganizationGroupPolicyHistory, buildOrganizationProfileDocumentPatch, buildRosterProfileDocumentPatch, checkNativePurchaseEligibility, createInitializedRosterProfileDocument, createOrganizationGroup, DEFAULT_PERSONAL_ORGANIZATION_PROFILE_NAME, deriveOrganizationMetadataContainerSystemSlot, deriveOrganizationRosterProfileContainerSystemSlot, discardUnreadableJournaledPrincipalMutation, GroupMembershipNameMismatchError, getOrganizationProfileDocumentLocalId, getRosterProfileDocumentLocalId, importOrganizationUser, loadLocalOrganizationDataUsage, loadLocalOrganizationDirectoryAndGroups, loadOrganizationBilling, loadOrganizationBillingHistory, loadOrganizationBillingManagementUrl, ORGANIZATION_METADATA_CONTAINER_NAME, ORGANIZATION_PROFILE_DOCUMENT_KIND, ORGANIZATION_ROSTER_PROFILE_CONTAINER_NAME, PendingPrincipalMutationError, PrincipalMutationOutcomeUnknownError, ROSTER_PROFILE_DOCUMENT_KIND, readJournaledPrincipalMutation, readOrganizationProfileName, reconcileOrganizationDataUsage, reconcileOrganizationDirectoryAndGroups, recoverJournaledPrincipalMutation, removeOrganizationGroupUser, resolveOrganizationBillingView, retainAcknowledgedPrincipalCurrents, revokeOrganizationContainerGrant, startOrganizationTrial, submitJournaledPrincipalMutation, subscribeOrganizationReadModelInvalidation, UnreadablePrincipalMutationError, updateOrganizationProfile, updateOrganizationRosterEntry, } from "./workflows/organizations/index.js";
19
19
  export type { CacheReferencedPrincipalPoliciesOptions, PrincipalHistoryProtectionLease, ProjectionPolicyHistoryRecoveryOptions, RecoveredPrincipalPolicyHistory, RecoveredScopedPrincipalPolicyHistory, RecoverPrincipalPolicyHistoryOptions, RecoverScopedPrincipalPolicyHistoryOptions, } from "./workflows/principals/index.js";
20
20
  export { cacheReferencedPrincipalPolicies, PrincipalPolicyHistoryReadError, recoverPrincipalPolicyHistory, recoverProjectionPolicyHistory, recoverScopedPrincipalPolicyHistory, } from "./workflows/principals/index.js";