@party-stack/foundry-ontology 0.19.0 → 0.20.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.
Files changed (202) hide show
  1. package/README.md +53 -32
  2. package/lib/adapter/backend.d.ts +35 -0
  3. package/lib/adapter/backend.d.ts.map +1 -0
  4. package/lib/adapter/backend.js +210 -0
  5. package/lib/adapter/backend.js.map +1 -0
  6. package/lib/adapter/encrypted.d.ts +12 -0
  7. package/lib/adapter/encrypted.d.ts.map +1 -0
  8. package/lib/adapter/encrypted.js +79 -0
  9. package/lib/adapter/encrypted.js.map +1 -0
  10. package/lib/adapter/foundryActionError.d.ts +7 -0
  11. package/lib/adapter/foundryActionError.d.ts.map +1 -0
  12. package/lib/adapter/foundryActionError.js +61 -0
  13. package/lib/adapter/foundryActionError.js.map +1 -0
  14. package/lib/adapter/foundryActionValidation.d.ts +4 -3
  15. package/lib/adapter/foundryActionValidation.d.ts.map +1 -1
  16. package/lib/adapter/foundryActionValidation.js +71 -25
  17. package/lib/adapter/foundryActionValidation.js.map +1 -1
  18. package/lib/adapter/foundryCodec.d.ts +2 -5
  19. package/lib/adapter/foundryCodec.d.ts.map +1 -1
  20. package/lib/adapter/foundryCodec.js +19 -7
  21. package/lib/adapter/foundryCodec.js.map +1 -1
  22. package/lib/adapter/foundryObjectSearch.d.ts +13 -0
  23. package/lib/adapter/foundryObjectSearch.d.ts.map +1 -0
  24. package/lib/adapter/foundryObjectSearch.js +57 -0
  25. package/lib/adapter/foundryObjectSearch.js.map +1 -0
  26. package/lib/adapter/index.d.ts +3 -4
  27. package/lib/adapter/index.js +3 -4
  28. package/lib/adapter/likeFilter.d.ts +10 -0
  29. package/lib/adapter/likeFilter.d.ts.map +1 -0
  30. package/lib/adapter/likeFilter.js +87 -0
  31. package/lib/adapter/likeFilter.js.map +1 -0
  32. package/lib/adapter/makeFoundryOntologyBackend.d.ts +76 -0
  33. package/lib/adapter/makeFoundryOntologyBackend.d.ts.map +1 -0
  34. package/lib/adapter/makeFoundryOntologyBackend.js +505 -0
  35. package/lib/adapter/makeFoundryOntologyBackend.js.map +1 -0
  36. package/lib/adapter/subsetFilter.d.ts +14 -0
  37. package/lib/adapter/subsetFilter.d.ts.map +1 -0
  38. package/lib/adapter/subsetFilter.js +105 -0
  39. package/lib/adapter/subsetFilter.js.map +1 -0
  40. package/lib/adapter/uploads.d.ts +25 -0
  41. package/lib/adapter/uploads.d.ts.map +1 -0
  42. package/lib/adapter/uploads.js +78 -0
  43. package/lib/adapter/uploads.js.map +1 -0
  44. package/lib/backend.d.ts +80 -0
  45. package/lib/backend.d.ts.map +1 -0
  46. package/lib/backend.js +34 -0
  47. package/lib/backend.js.map +1 -0
  48. package/lib/config/index.d.ts +8 -12
  49. package/lib/config/index.d.ts.map +1 -1
  50. package/lib/config/index.js +49 -52
  51. package/lib/config/index.js.map +1 -1
  52. package/lib/connectionOptions.d.ts +9 -0
  53. package/lib/connectionOptions.d.ts.map +1 -0
  54. package/lib/connectionOptions.js +1 -0
  55. package/lib/context.d.ts +21 -0
  56. package/lib/context.d.ts.map +1 -0
  57. package/lib/context.js +16 -0
  58. package/lib/context.js.map +1 -0
  59. package/lib/index.d.ts +7 -7
  60. package/lib/index.js +6 -7
  61. package/lib/meta/backend.d.ts +10 -0
  62. package/lib/meta/backend.d.ts.map +1 -0
  63. package/lib/meta/backend.js +161 -0
  64. package/lib/meta/backend.js.map +1 -0
  65. package/lib/meta/convertMetaActionType.d.ts +3 -2
  66. package/lib/meta/convertMetaActionType.d.ts.map +1 -1
  67. package/lib/meta/convertMetaActionType.js +3 -3
  68. package/lib/meta/convertMetaActionType.js.map +1 -1
  69. package/lib/meta/convertMetaLinkType.d.ts +2 -2
  70. package/lib/meta/convertMetaLinkType.d.ts.map +1 -1
  71. package/lib/meta/convertMetaLinkType.js +13 -13
  72. package/lib/meta/convertMetaLinkType.js.map +1 -1
  73. package/lib/meta/convertMetaObjectType.d.ts +2 -2
  74. package/lib/meta/convertMetaObjectType.d.ts.map +1 -1
  75. package/lib/meta/convertMetaObjectType.js +5 -1
  76. package/lib/meta/convertMetaObjectType.js.map +1 -1
  77. package/lib/meta/convertMetaQueryFunctionType.d.ts +1 -1
  78. package/lib/meta/convertMetaQueryFunctionType.js.map +1 -1
  79. package/lib/meta/convertMetaTypeDef.d.ts +1 -1
  80. package/lib/meta/convertMetaTypeDef.d.ts.map +1 -1
  81. package/lib/meta/convertMetaTypeDef.js +7 -1
  82. package/lib/meta/convertMetaTypeDef.js.map +1 -1
  83. package/lib/meta/convertMetaValueType.d.ts +2 -2
  84. package/lib/meta/convertMetaValueType.d.ts.map +1 -1
  85. package/lib/meta/convertMetaValueType.js.map +1 -1
  86. package/lib/meta/convertOmsActionParameterMetadata.d.ts +1 -1
  87. package/lib/meta/convertOmsActionParameterMetadata.js +1 -1
  88. package/lib/meta/convertOmsActionParameterMetadata.js.map +1 -1
  89. package/lib/meta/index.d.ts +2 -4
  90. package/lib/meta/index.js +2 -4
  91. package/lib/meta/listObjectTypes.d.ts +6 -0
  92. package/lib/meta/listObjectTypes.d.ts.map +1 -0
  93. package/lib/meta/listObjectTypes.js +13 -0
  94. package/lib/meta/listObjectTypes.js.map +1 -0
  95. package/lib/meta/loadActions.d.ts +7 -0
  96. package/lib/meta/loadActions.d.ts.map +1 -0
  97. package/lib/meta/loadActions.js +20 -0
  98. package/lib/meta/loadActions.js.map +1 -0
  99. package/lib/meta/loadEntities.d.ts +9 -0
  100. package/lib/meta/loadEntities.d.ts.map +1 -0
  101. package/lib/meta/loadEntities.js +73 -0
  102. package/lib/meta/loadEntities.js.map +1 -0
  103. package/lib/meta/loadQueryFunctions.d.ts +11 -0
  104. package/lib/meta/loadQueryFunctions.d.ts.map +1 -0
  105. package/lib/meta/loadQueryFunctions.js +18 -0
  106. package/lib/meta/loadQueryFunctions.js.map +1 -0
  107. package/lib/meta/planMetaLookup.d.ts +12 -0
  108. package/lib/meta/planMetaLookup.d.ts.map +1 -0
  109. package/lib/meta/planMetaLookup.js +44 -0
  110. package/lib/meta/planMetaLookup.js.map +1 -0
  111. package/lib/platform.d.ts +78 -0
  112. package/lib/platform.d.ts.map +1 -0
  113. package/lib/platform.js +116 -0
  114. package/lib/platform.js.map +1 -0
  115. package/lib/platformCollections.d.ts +68 -0
  116. package/lib/platformCollections.d.ts.map +1 -0
  117. package/lib/platformCollections.js +7 -0
  118. package/lib/platformCollections.js.map +1 -0
  119. package/lib/serviceConnector.d.ts +8 -0
  120. package/lib/serviceConnector.d.ts.map +1 -0
  121. package/lib/serviceConnector.js +105 -0
  122. package/lib/serviceConnector.js.map +1 -0
  123. package/lib/stagedWrites.d.ts +3 -2
  124. package/lib/stagedWrites.d.ts.map +1 -1
  125. package/lib/stagedWrites.js +30 -3
  126. package/lib/stagedWrites.js.map +1 -1
  127. package/lib/users/admin.d.ts +105 -0
  128. package/lib/users/admin.d.ts.map +1 -0
  129. package/lib/users/admin.js +35 -0
  130. package/lib/users/admin.js.map +1 -0
  131. package/lib/users/createFoundryUsersIntegration.d.ts +2 -2
  132. package/lib/users/createFoundryUsersIntegration.d.ts.map +1 -1
  133. package/lib/users/createFoundryUsersIntegration.js +4 -6
  134. package/lib/users/createFoundryUsersIntegration.js.map +1 -1
  135. package/lib/users/foundryUser.d.ts +2 -2
  136. package/lib/users/foundryUser.js +1 -1
  137. package/lib/users/foundryUser.js.map +1 -1
  138. package/lib/users/index.d.ts +2 -3
  139. package/lib/users/index.js +2 -3
  140. package/lib/users/loadUserSubset.d.ts +8 -0
  141. package/lib/users/loadUserSubset.d.ts.map +1 -0
  142. package/lib/users/loadUserSubset.js +166 -0
  143. package/lib/users/loadUserSubset.js.map +1 -0
  144. package/lib/utils/AsyncIterable.d.ts +1 -1
  145. package/lib/utils/AsyncIterable.d.ts.map +1 -1
  146. package/lib/utils/AsyncIterable.js +11 -0
  147. package/lib/utils/AsyncIterable.js.map +1 -1
  148. package/package.json +16 -15
  149. package/lib/adapter/convertLoadSubsetOptions.d.ts +0 -8
  150. package/lib/adapter/convertLoadSubsetOptions.d.ts.map +0 -1
  151. package/lib/adapter/convertLoadSubsetOptions.js +0 -178
  152. package/lib/adapter/convertLoadSubsetOptions.js.map +0 -1
  153. package/lib/adapter/createFoundryOntologyBackendAdapter.d.ts +0 -29
  154. package/lib/adapter/createFoundryOntologyBackendAdapter.d.ts.map +0 -1
  155. package/lib/adapter/createFoundryOntologyBackendAdapter.js +0 -311
  156. package/lib/adapter/createFoundryOntologyBackendAdapter.js.map +0 -1
  157. package/lib/adapter/objectCollectionOptions.d.ts +0 -33
  158. package/lib/adapter/objectCollectionOptions.d.ts.map +0 -1
  159. package/lib/adapter/objectCollectionOptions.js +0 -482
  160. package/lib/adapter/objectCollectionOptions.js.map +0 -1
  161. package/lib/config/applyAttachmentConstraintOverrides.d.ts +0 -10
  162. package/lib/config/applyAttachmentConstraintOverrides.d.ts.map +0 -1
  163. package/lib/config/applyAttachmentConstraintOverrides.js +0 -50
  164. package/lib/config/applyAttachmentConstraintOverrides.js.map +0 -1
  165. package/lib/connection.d.ts +0 -38
  166. package/lib/connection.d.ts.map +0 -1
  167. package/lib/connection.js +0 -195
  168. package/lib/connection.js.map +0 -1
  169. package/lib/installation/createFoundryBackendInstallation.d.ts +0 -29
  170. package/lib/installation/createFoundryBackendInstallation.d.ts.map +0 -1
  171. package/lib/installation/createFoundryBackendInstallation.js +0 -60
  172. package/lib/installation/createFoundryBackendInstallation.js.map +0 -1
  173. package/lib/installation/index.d.ts +0 -2
  174. package/lib/installation/index.js +0 -1
  175. package/lib/meta/actionTypeCollectionOptions.d.ts +0 -14
  176. package/lib/meta/actionTypeCollectionOptions.d.ts.map +0 -1
  177. package/lib/meta/actionTypeCollectionOptions.js +0 -58
  178. package/lib/meta/actionTypeCollectionOptions.js.map +0 -1
  179. package/lib/meta/convertActionTypeLoadSubsetOptions.d.ts +0 -7
  180. package/lib/meta/convertActionTypeLoadSubsetOptions.d.ts.map +0 -1
  181. package/lib/meta/convertActionTypeLoadSubsetOptions.js +0 -84
  182. package/lib/meta/convertActionTypeLoadSubsetOptions.js.map +0 -1
  183. package/lib/meta/createFoundryMetaOntologyBackendAdapter.d.ts +0 -11
  184. package/lib/meta/createFoundryMetaOntologyBackendAdapter.d.ts.map +0 -1
  185. package/lib/meta/createFoundryMetaOntologyBackendAdapter.js +0 -43
  186. package/lib/meta/createFoundryMetaOntologyBackendAdapter.js.map +0 -1
  187. package/lib/meta/entityCollectionOptions.d.ts +0 -25
  188. package/lib/meta/entityCollectionOptions.d.ts.map +0 -1
  189. package/lib/meta/entityCollectionOptions.js +0 -69
  190. package/lib/meta/entityCollectionOptions.js.map +0 -1
  191. package/lib/meta/queryFunctionTypeCollectionOptions.d.ts +0 -12
  192. package/lib/meta/queryFunctionTypeCollectionOptions.d.ts.map +0 -1
  193. package/lib/meta/queryFunctionTypeCollectionOptions.js +0 -59
  194. package/lib/meta/queryFunctionTypeCollectionOptions.js.map +0 -1
  195. package/lib/users/createFoundryAdminOntologyRoute.d.ts +0 -55
  196. package/lib/users/createFoundryAdminOntologyRoute.d.ts.map +0 -1
  197. package/lib/users/createFoundryAdminOntologyRoute.js +0 -33
  198. package/lib/users/createFoundryAdminOntologyRoute.js.map +0 -1
  199. package/lib/users/userCollectionOptions.d.ts +0 -25
  200. package/lib/users/userCollectionOptions.d.ts.map +0 -1
  201. package/lib/users/userCollectionOptions.js +0 -94
  202. package/lib/users/userCollectionOptions.js.map +0 -1
package/README.md CHANGED
@@ -1,28 +1,50 @@
1
1
  # foundry-ontology
2
2
 
3
- Foundry adapter and metadata conversion for Party Stack LiveOntology.
3
+ Native Foundry data, metadata, and account-connection capabilities.
4
4
 
5
- ## Metadata polling
5
+ ## Metadata queries
6
6
 
7
- Set `metaRefetchInterval` on a metadata-only route included in your Foundry installation:
7
+ Open a native metadata backend with selected Connect egress:
8
8
 
9
9
  ```ts
10
- createFoundryOntologyRoute({
11
- ontologyId: "ri.ontology.main.ontology.example",
12
- metaRefetchInterval: 30_000,
13
- });
10
+ import { makeFoundryMetadataBackend } from "@party-stack/foundry-ontology";
11
+
12
+ const backend =
13
+ yield *
14
+ makeFoundryMetadataBackend({
15
+ baseUrl,
16
+ ontologyId: "ri.ontology.main.ontology.example",
17
+ egress,
18
+ refetchInterval: 30_000,
19
+ });
14
20
  ```
15
21
 
16
- Calling `installation.openMetaOntology({ userId, ontologyId })` uses that route's
17
- interval for the shared object/value/link metadata collection, action types, and
18
- query function types. The interval is in milliseconds; `false` disables polling,
19
- and omitting it preserves the default of no polling. Repeated opens return the
20
- cached meta ontology with the same route configuration. Routes with supplied `ir`
21
- project local metadata and do not poll Foundry.
22
-
23
- You can also set `refetchInterval` directly on
24
- `createFoundryMetaOntologyBackendAdapter` when creating a meta ontology without an
25
- installation.
22
+ Object-type name and RID filters use Foundry's batch lookup endpoints. Value-type
23
+ name filters use direct lookups. Link queries constrained by an object type load
24
+ that type's outgoing links and the inverse definitions needed to reconstruct each
25
+ link. Unbounded queries use the corresponding listing endpoints.
26
+
27
+ The backend narrows provider requests where it can prove they include every
28
+ matching row. The shared query engine then applies residual predicates, ordering,
29
+ and limits. A bounded fetch never certifies that the whole object type is loaded.
30
+
31
+ `refetchInterval` supplies a recommended polling interval, in milliseconds. The
32
+ replica schedules polling; the adapter does not run a separate timer. `false`
33
+ disables the recommendation, and omission defaults to no polling. An explicit
34
+ replica demand refresh policy takes precedence.
35
+
36
+ Metadata is an ordinary backend, usable directly for native pull or
37
+ with the shared meta ontology IR in a replica. `makeFoundryBackend` opens a data
38
+ backend; `makeFoundryAdminBackend` from the `users` export opens the explicit users
39
+ ontology. These helpers do not create SDK views or installations.
40
+
41
+ For saved connections, register `makeFoundryServiceConnector` with Connect and
42
+ acquire authenticated egress through `connect.connections.acquire(connectionRef)`.
43
+ Pass that egress to the normal backend constructor. `makeOntologyServer` routes
44
+ Connect and managed replicas through the same ontology protocol;
45
+ `serveOntologyWorker(server)` binds it to a worker transport. In tabs, `createAuthenticationClient`
46
+ handles authentication and `OntologyEnvironmentProvider` opens named views. See the
47
+ [Foundry guide](../../apps/website/content/docs/foundry.mdx) for the complete setup.
26
48
 
27
49
  ## Link metadata
28
50
 
@@ -45,21 +67,20 @@ are preserved as suggestions. Object-query and struct-field prefills are omitted
45
67
  until the expression model supports them, and failures from the private endpoint
46
68
  do not prevent public action metadata from loading.
47
69
 
48
- ## Refreshing objects
49
-
50
- Foundry object collections expose an awaitable full-source refresh:
70
+ ## Native backend integration
51
71
 
52
- ```ts
53
- await ontology.objects.Employee.utils.refresh?.();
54
- ```
72
+ Object, metadata, and Admin Users reads use the native backend's Effect-valued
73
+ `loadSubset` interface. Replica owns demand, persistent rows, action receipts,
74
+ confirmation reads, and optimistic state; TanStack collections materialize that
75
+ state for applications. Provider collection-option helpers are no longer exported.
55
76
 
56
- `refresh` is an optional adapter utility on the generic ontology collection API.
57
- Foundry fetches a fresh snapshot and replaces cached rows in one sync transaction,
58
- while keeping live subscriptions active and applying updates received during the
59
- fetch. The promise waits for the commit and rejects on failure. Ontology
60
- collections start their sync before refreshing; when using
61
- `objectCollectionOptions` directly, preload the collection first.
77
+ The Foundry backend uses the scoped Effect object-set watcher. Watch messages are
78
+ invalidation hints; Replica refreshes the affected demand. They do not establish a
79
+ durable edits-history cursor. Actions save receipts before confirmation reads, so
80
+ a failed read does not cause the action to be submitted again.
62
81
 
63
- When edit history is unavailable, reconnection and action acknowledgement also
64
- request a fresh snapshot. Acknowledgement waits for that snapshot's commit; it
65
- cannot confirm a specific edit-history operation ID in this mode.
82
+ `makeFoundryServiceConnector(options)` registers native OAuth exchange, refresh,
83
+ identity verification, and authenticated HTTP/WebSocket egress. Credentials remain
84
+ beside the backend. Interactive OAuth uses the ordinary Connect actions and the
85
+ tab runtime's browser presentation capability. Opening a saved connection does
86
+ not start interactive authentication.
@@ -0,0 +1,35 @@
1
+ import { OntologyClient } from "@party-stack/foundry-client";
2
+ import { Effect } from "effect";
3
+ import { Actions } from "@osdk/foundry.ontologies";
4
+ import { ActionRequest, BackendError, BackendSnapshot, OntologyBackendService } from "@party-stack/ontology-backend";
5
+ import { OntologyIR } from "@party-stack/ontology-spec";
6
+ import { Query } from "@party-stack/ontology-storage";
7
+ //#region src/adapter/backend.d.ts
8
+ /** One scoped watcher multiplexes the live object types used by this binding.
9
+ * Watch messages invalidate snapshots; they do not pretend to be durable journal cursors.
10
+ */
11
+ export declare const makeFoundryBackend: (options: {
12
+ client: OntologyClient;
13
+ ir: OntologyIR;
14
+ context: Record<string, unknown>;
15
+ watch?: boolean;
16
+ users?: {
17
+ objectType: string;
18
+ loadSubset: (query: Query, signal: AbortSignal) => Promise<BackendSnapshot>;
19
+ };
20
+ submitAction: (invocation: ActionRequest, signal: AbortSignal) => Promise<{
21
+ result: Awaited<ReturnType<typeof Actions.applyWithOverrides>> & {
22
+ operationId?: string;
23
+ };
24
+ attachmentIdMappings: readonly {
25
+ localId: string;
26
+ remoteId: string;
27
+ }[];
28
+ }>;
29
+ queryFunction?: (name: string, parameters: Record<string, unknown>, signal: AbortSignal) => Promise<unknown>;
30
+ attachments?: OntologyBackendService["attachments"];
31
+ encrypted?: OntologyBackendService["encrypted"];
32
+ decodeObject: (objectType: string, value: Record<string, unknown>) => Record<string, unknown>;
33
+ }) => Effect.Effect<OntologyBackendService, BackendError<"Conflict" | "Forbidden" | "HistoryExpired" | "Internal" | "InvalidConfiguration" | "InvalidRequest" | "InvalidResponse" | "NotFound" | "NotImplemented" | "RateLimited" | "ResultExpired" | "SchemaMismatch" | "SessionExpired" | "SessionReplaced" | "SourceMismatch" | "Unauthenticated" | "Unavailable" | "ValidationFailed" | "ViewReplaced">, import("effect/Scope").Scope>;
34
+ //#endregion
35
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","names":[],"sources":["../../src/adapter/backend.ts"],"mappings":";;;;;;;;;;qBAwGa,qBAAkB;EACnB,QAAA;EACJ,IAAA;EACK,SAAA;EACD;EACA;IACJ;IACA,aAAa,OAAO,OAAO,QAAQ,gBAAgB,QAAQ;;EAEjD,eACV,YAAY,eACZ,QAAQ,gBACP;IACD,QAAQ,QAAQ,kBAAkB,QAAQ;MAAyB;;IACnE;MAAiC;MAAiB;;;EAEtC,iBACZ,cACA,YAAY,yBACZ,QAAQ,gBACP;EACS,cAAA;EACF,YAAA;EACE,eAAC,oBAAoB,OAAO,4BAA4B;MAuMxE,OAAA,OAAA,wBAAA,wXAAA"}
@@ -0,0 +1,210 @@
1
+ import { searchFoundryObjects } from "./foundryObjectSearch.js";
2
+ import { analyzeSubsetFilter, subsetFilter } from "./subsetFilter.js";
3
+ import { Deferred, Effect, Queue, Redacted, Schema, Stream } from "effect";
4
+ import { BackendError, classifyBackendFailure, makeOntologyBackend } from "@party-stack/ontology-backend";
5
+ import { makeObjectSetWatcher } from "@party-stack/foundry-object-set-watcher";
6
+ import { compileObjectSubset, objectSubset } from "@party-stack/ontology-backend/subset";
7
+ import { QuerySchema } from "@party-stack/ontology-storage";
8
+ import { isField } from "@party-stack/ontology-storage/tables";
9
+ //#region src/adapter/backend.ts
10
+ const KeySchema = Schema.Union([Schema.String, Schema.Finite]);
11
+ const ObjectTarget = Schema.Struct({
12
+ objectType: Schema.String,
13
+ primaryKey: Schema.Unknown
14
+ });
15
+ const ActionResult = Schema.Struct({
16
+ operationId: Schema.optional(Schema.String),
17
+ edits: Schema.optional(Schema.Union([Schema.Struct({
18
+ type: Schema.Literal("largeScaleEdits"),
19
+ editedObjectTypes: Schema.Array(Schema.String)
20
+ }), Schema.Struct({
21
+ type: Schema.Literal("edits"),
22
+ edits: Schema.Array(Schema.Union([Schema.Struct({
23
+ type: Schema.Literals([
24
+ "addObject",
25
+ "modifyObject",
26
+ "deleteObject"
27
+ ]),
28
+ ...ObjectTarget.fields
29
+ }), Schema.Struct({
30
+ type: Schema.Literals(["addLink", "deleteLink"]),
31
+ aSideObject: ObjectTarget,
32
+ bSideObject: ObjectTarget
33
+ })]))
34
+ })]))
35
+ });
36
+ const invalidResponse = (operation, cause) => new BackendError({
37
+ operation,
38
+ kind: "InvalidResponse",
39
+ message: "Foundry returned an invalid response.",
40
+ retryable: false,
41
+ actionOutcome: "unknown",
42
+ cause: Redacted.make(cause)
43
+ });
44
+ const unavailable = (cause) => classifyBackendFailure("foundry.sync", cause);
45
+ /** Each logical object set has its own acknowledgement. Pump before returning so
46
+ * changes between that acknowledgement and the snapshot read remain observable.
47
+ */
48
+ const acquireWatch = Effect.fn("Foundry.acquireWatch")(function* (messages) {
49
+ const ready = yield* Deferred.make();
50
+ const events = yield* Effect.acquireRelease(Queue.sliding(1), (queue) => Queue.shutdown(queue).pipe(Effect.asVoid));
51
+ const interrupted = () => new BackendError({
52
+ operation: "foundry.watchSubset",
53
+ kind: "Unavailable",
54
+ retryable: true,
55
+ message: "The Foundry object-set subscription is no longer active."
56
+ });
57
+ const receive = Stream.runForEach(messages, (message) => {
58
+ if (message.type !== "state") return Queue.offer(events, void 0).pipe(Effect.asVoid);
59
+ return message.status === "open" ? Deferred.succeed(ready, void 0).pipe(Effect.asVoid) : Effect.fail(interrupted());
60
+ }).pipe(Effect.mapError(unavailable), Effect.andThen(Effect.fail(interrupted())), Effect.catchCause((cause) => Deferred.failCause(ready, cause).pipe(Effect.andThen(Queue.failCause(events, cause)), Effect.asVoid)));
61
+ yield* Effect.forkScoped(receive, { startImmediately: true });
62
+ yield* Deferred.await(ready);
63
+ return Stream.fromQueue(events);
64
+ });
65
+ /** One scoped watcher multiplexes the live object types used by this binding.
66
+ * Watch messages invalidate snapshots; they do not pretend to be durable journal cursors.
67
+ */
68
+ const makeFoundryBackend = Effect.fn("FoundryBackend.make")(function* (options) {
69
+ const watcher = options.watch === false ? void 0 : yield* makeObjectSetWatcher(options.client).pipe(Effect.mapError(unavailable));
70
+ return makeOntologyBackend({
71
+ ir: options.ir,
72
+ context: options.context,
73
+ name: "foundry",
74
+ sourceId: `foundry:${options.client.baseUrl}:${options.client.ontologyRid}`,
75
+ supportsIdempotency: false,
76
+ queryFunction: (name, parameters) => options.queryFunction ? Effect.tryPromise({
77
+ try: (signal) => options.queryFunction(name, parameters, signal),
78
+ catch: unavailable
79
+ }) : Effect.fail(new BackendError({
80
+ operation: "queryFunction",
81
+ kind: "NotImplemented",
82
+ message: "No Foundry query function implementation.",
83
+ retryable: false
84
+ })),
85
+ attachments: options.attachments,
86
+ encrypted: options.encrypted,
87
+ loadSubset: Effect.fn("Foundry.loadSubset")(function* (request) {
88
+ const subset = yield* compileObjectSubset(options.ir, request, options.context);
89
+ yield* Effect.annotateCurrentSpan("objectType", subset.objectType);
90
+ yield* Schema.decodeUnknownEffect(QuerySchema)(subset.query ?? {}).pipe(Effect.mapError((cause) => new BackendError({
91
+ operation: "foundry.loadSubset",
92
+ kind: "InvalidRequest",
93
+ message: "Invalid subset query.",
94
+ retryable: false,
95
+ cause: Redacted.make(cause)
96
+ })));
97
+ if (subset.objectType === options.users?.objectType) return yield* Effect.tryPromise({
98
+ try: (signal) => options.users.loadSubset(subset.query ?? {}, signal),
99
+ catch: unavailable
100
+ });
101
+ const definition = options.ir.objectTypes.find((type) => type.name === subset.objectType);
102
+ if (!definition) return yield* new BackendError({
103
+ operation: "foundry.loadSubset",
104
+ kind: "InvalidRequest",
105
+ message: "Unknown Foundry object type.",
106
+ retryable: false
107
+ });
108
+ const predicate = subset.query?.where;
109
+ const candidates = predicate?.op === "in" && isField(predicate.left) && predicate.left.path.length === 1 && predicate.left.path[0] === definition.primaryKey ? predicate.values : predicate?.op === "eq" && isField(predicate.left) && predicate.left.path.length === 1 && predicate.left.path[0] === definition.primaryKey && !isField(predicate.right) ? [predicate.right] : void 0;
110
+ const keys = candidates?.every(Schema.is(KeySchema)) ? candidates : void 0;
111
+ const plan = analyzeSubsetFilter(predicate);
112
+ const pushed = keys ? {
113
+ type: "in",
114
+ field: definition.primaryKey,
115
+ value: [...new Set(keys)]
116
+ } : plan.query;
117
+ const values = yield* Effect.tryPromise({
118
+ try: (signal) => searchFoundryObjects(options.client, subset.objectType, definition.properties.map((property) => property.name), {
119
+ signal,
120
+ where: pushed
121
+ }, (value) => options.decodeObject(subset.objectType, value)),
122
+ catch: unavailable
123
+ });
124
+ const rows = yield* Effect.forEach(values, (value) => Schema.decodeUnknownEffect(KeySchema)(value[definition.primaryKey]).pipe(Effect.map((key) => ({
125
+ key,
126
+ value
127
+ })), Effect.mapError((cause) => invalidResponse("foundry.loadSubset", cause))));
128
+ return {
129
+ fulfilledSubset: objectSubset(subset.objectType, { where: subset.query?.where }),
130
+ fetchedRows: values.length,
131
+ pushdown: {
132
+ source: "Foundry",
133
+ predicates: plan.predicates.map((predicate) => ({
134
+ ...predicate,
135
+ execution: predicate.execution === "adapter" ? "engine" : predicate.execution
136
+ })),
137
+ sorting: subset.query?.orderBy?.length ? "engine" : "none",
138
+ pagination: subset.query?.limit !== void 0 || subset.query?.offset !== void 0 || subset.query?.after ? "engine" : "none",
139
+ rechecksFilter: Boolean(predicate),
140
+ native: {
141
+ language: "json",
142
+ text: JSON.stringify(pushed ?? {
143
+ type: "and",
144
+ value: []
145
+ }, null, 2)
146
+ }
147
+ },
148
+ rows,
149
+ complete: true
150
+ };
151
+ }),
152
+ applyAction: Effect.fn("Foundry.applyAction")(function* (invocation) {
153
+ const { result: rawResult, attachmentIdMappings } = yield* Effect.tryPromise({
154
+ try: (signal) => options.submitAction(invocation, signal),
155
+ catch: (cause) => classifyBackendFailure("foundry.applyAction", cause)
156
+ });
157
+ const result = yield* Schema.decodeUnknownEffect(ActionResult)(rawResult).pipe(Effect.mapError((cause) => invalidResponse("foundry.applyAction", cause)));
158
+ return yield* Effect.try({
159
+ try: () => {
160
+ const targets = /* @__PURE__ */ new Map();
161
+ const add = (objectType, rawKey) => {
162
+ const definition = options.ir.objectTypes.find((type) => type.name === objectType);
163
+ if (!definition || objectType === options.users?.objectType) return;
164
+ const key = rawKey === void 0 ? void 0 : options.decodeObject(objectType, { [definition.primaryKey]: rawKey })[definition.primaryKey];
165
+ if (rawKey === void 0) targets.set(objectType, void 0);
166
+ else if (!Schema.is(KeySchema)(key)) throw new Error("Invalid edit identity.");
167
+ else if (!targets.has(objectType) || targets.get(objectType)) targets.set(objectType, /* @__PURE__ */ new Set([...targets.get(objectType) ?? [], key]));
168
+ };
169
+ if (!result.edits) for (const type of options.ir.objectTypes) add(type.name);
170
+ else if (result.edits.type === "largeScaleEdits") for (const type of result.edits.editedObjectTypes) add(type);
171
+ else for (const edit of result.edits.edits) if ("aSideObject" in edit) {
172
+ add(edit.aSideObject.objectType, edit.aSideObject.primaryKey);
173
+ add(edit.bSideObject.objectType, edit.bSideObject.primaryKey);
174
+ } else add(edit.objectType, edit.primaryKey);
175
+ return {
176
+ operationId: invocation.id,
177
+ outcome: "committed",
178
+ providerOperationId: result.operationId,
179
+ affected: [...targets].map(([objectType, keys]) => ({
180
+ objectType,
181
+ ...keys ? { keys: [...keys] } : {}
182
+ })),
183
+ attachmentIdMappings: attachmentIdMappings.length ? attachmentIdMappings : void 0
184
+ };
185
+ },
186
+ catch: (cause) => invalidResponse("foundry.applyAction", cause)
187
+ });
188
+ }),
189
+ refresh: (subset) => subset.from.source.objectType === options.users?.objectType ? { pollIntervalMs: 6e4 } : void 0,
190
+ watchSubset: watcher ? (request) => request.from.source.objectType === options.users?.objectType ? void 0 : Effect.gen(function* () {
191
+ const subset = yield* compileObjectSubset(options.ir, request, options.context);
192
+ const filter = subsetFilter(subset.query?.where);
193
+ return yield* acquireWatch(watcher.subscribe(filter ? {
194
+ type: "filter",
195
+ objectSet: {
196
+ type: "base",
197
+ objectType: subset.objectType
198
+ },
199
+ where: filter
200
+ } : {
201
+ type: "base",
202
+ objectType: subset.objectType
203
+ }));
204
+ }) : void 0
205
+ });
206
+ });
207
+ //#endregion
208
+ export { makeFoundryBackend };
209
+
210
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","names":[],"sources":["../../src/adapter/backend.ts"],"sourcesContent":["import { makeObjectSetWatcher } from \"@party-stack/foundry-object-set-watcher\";\nimport { makeOntologyBackend } from \"@party-stack/ontology-backend\";\nimport { BackendError, classifyBackendFailure } from \"@party-stack/ontology-backend\";\nimport { compileObjectSubset, objectSubset } from \"@party-stack/ontology-backend/subset\";\nimport { QuerySchema } from \"@party-stack/ontology-storage\";\nimport { isField } from \"@party-stack/ontology-storage/tables\";\nimport { Deferred, Effect, Queue, Schema, Stream, Redacted } from \"effect\";\nimport { searchFoundryObjects } from \"./foundryObjectSearch.js\";\nimport { analyzeSubsetFilter, subsetFilter } from \"./subsetFilter.js\";\nimport type { Actions } from \"@osdk/foundry.ontologies\";\nimport type { OntologyClient } from \"@party-stack/foundry-client\";\nimport type { ObjectSetSubscriptionMessage, WatcherError } from \"@party-stack/foundry-object-set-watcher\";\nimport type {\n OntologyBackendService,\n ActionRequest,\n ActionReceipt,\n SourceQueryPlan,\n SubsetWatchEvent,\n} from \"@party-stack/ontology-backend\";\nimport type { BackendSnapshot } from \"@party-stack/ontology-backend\";\nimport type { OntologyIR } from \"@party-stack/ontology-spec\";\nimport type { ObjectSubset } from \"@party-stack/ontology-spec/query\";\nimport type { Query } from \"@party-stack/ontology-storage\";\n\nconst KeySchema = Schema.Union([Schema.String, Schema.Finite]);\nconst ObjectTarget = Schema.Struct({ objectType: Schema.String, primaryKey: Schema.Unknown });\nconst ActionResult = Schema.Struct({\n operationId: Schema.optional(Schema.String),\n edits: Schema.optional(\n Schema.Union([\n Schema.Struct({\n type: Schema.Literal(\"largeScaleEdits\"),\n editedObjectTypes: Schema.Array(Schema.String),\n }),\n Schema.Struct({\n type: Schema.Literal(\"edits\"),\n edits: Schema.Array(\n Schema.Union([\n Schema.Struct({\n type: Schema.Literals([\"addObject\", \"modifyObject\", \"deleteObject\"]),\n ...ObjectTarget.fields,\n }),\n Schema.Struct({\n type: Schema.Literals([\"addLink\", \"deleteLink\"]),\n aSideObject: ObjectTarget,\n bSideObject: ObjectTarget,\n }),\n ])\n ),\n }),\n ])\n ),\n});\nconst invalidResponse = (operation: string, cause: unknown) =>\n new BackendError({\n operation,\n kind: \"InvalidResponse\",\n message: \"Foundry returned an invalid response.\",\n retryable: false,\n actionOutcome: \"unknown\",\n cause: Redacted.make(cause),\n });\nconst unavailable = (cause: unknown) => classifyBackendFailure(\"foundry.sync\", cause);\n\n/** Each logical object set has its own acknowledgement. Pump before returning so\n * changes between that acknowledgement and the snapshot read remain observable.\n */\nconst acquireWatch = Effect.fn(\"Foundry.acquireWatch\")(function* (\n messages: Stream.Stream<ObjectSetSubscriptionMessage, WatcherError>\n) {\n const ready = yield* Deferred.make<void, BackendError>();\n const events = yield* Effect.acquireRelease(Queue.sliding<SubsetWatchEvent, BackendError>(1), (queue) =>\n Queue.shutdown(queue).pipe(Effect.asVoid)\n );\n const interrupted = () =>\n new BackendError({\n operation: \"foundry.watchSubset\",\n kind: \"Unavailable\",\n retryable: true,\n message: \"The Foundry object-set subscription is no longer active.\",\n });\n const receive = Stream.runForEach(messages, (message) => {\n if (message.type !== \"state\") return Queue.offer(events, undefined).pipe(Effect.asVoid);\n return message.status === \"open\"\n ? Deferred.succeed(ready, undefined).pipe(Effect.asVoid)\n : Effect.fail(interrupted());\n }).pipe(\n Effect.mapError(unavailable),\n Effect.andThen(Effect.fail(interrupted())),\n Effect.catchCause((cause) =>\n Deferred.failCause(ready, cause).pipe(\n Effect.andThen(Queue.failCause(events, cause)),\n Effect.asVoid\n )\n )\n );\n yield* Effect.forkScoped(receive, { startImmediately: true });\n yield* Deferred.await(ready);\n return Stream.fromQueue(events);\n});\n\n/** One scoped watcher multiplexes the live object types used by this binding.\n * Watch messages invalidate snapshots; they do not pretend to be durable journal cursors.\n */\nexport const makeFoundryBackend = Effect.fn(\"FoundryBackend.make\")(function* (options: {\n client: OntologyClient;\n ir: OntologyIR;\n context: Record<string, unknown>;\n watch?: boolean;\n users?: {\n objectType: string;\n loadSubset: (query: Query, signal: AbortSignal) => Promise<BackendSnapshot>;\n };\n submitAction: (\n invocation: ActionRequest,\n signal: AbortSignal\n ) => Promise<{\n result: Awaited<ReturnType<typeof Actions.applyWithOverrides>> & { operationId?: string };\n attachmentIdMappings: readonly { localId: string; remoteId: string }[];\n }>;\n queryFunction?: (\n name: string,\n parameters: Record<string, unknown>,\n signal: AbortSignal\n ) => Promise<unknown>;\n attachments?: OntologyBackendService[\"attachments\"];\n encrypted?: OntologyBackendService[\"encrypted\"];\n decodeObject: (objectType: string, value: Record<string, unknown>) => Record<string, unknown>;\n}) {\n const watcher =\n options.watch === false\n ? undefined\n : yield* makeObjectSetWatcher(options.client).pipe(Effect.mapError(unavailable));\n return makeOntologyBackend({\n ir: options.ir,\n context: options.context,\n name: \"foundry\",\n sourceId: `foundry:${options.client.baseUrl}:${options.client.ontologyRid}`,\n supportsIdempotency: false,\n queryFunction: (name: string, parameters: Record<string, unknown>) =>\n options.queryFunction\n ? Effect.tryPromise({\n try: (signal) => options.queryFunction!(name, parameters, signal),\n catch: unavailable,\n })\n : Effect.fail(\n new BackendError({\n operation: \"queryFunction\",\n kind: \"NotImplemented\",\n message: \"No Foundry query function implementation.\",\n retryable: false,\n })\n ),\n attachments: options.attachments,\n encrypted: options.encrypted,\n loadSubset: Effect.fn(\"Foundry.loadSubset\")(function* (request: ObjectSubset) {\n const subset = yield* compileObjectSubset(options.ir, request, options.context);\n yield* Effect.annotateCurrentSpan(\"objectType\", subset.objectType);\n yield* Schema.decodeUnknownEffect(QuerySchema)(subset.query ?? {}).pipe(\n Effect.mapError(\n (cause) =>\n new BackendError({\n operation: \"foundry.loadSubset\",\n kind: \"InvalidRequest\",\n message: \"Invalid subset query.\",\n retryable: false,\n cause: Redacted.make(cause),\n })\n )\n );\n if (subset.objectType === options.users?.objectType)\n return yield* Effect.tryPromise({\n try: (signal) => options.users!.loadSubset(subset.query ?? {}, signal),\n catch: unavailable,\n });\n const definition = options.ir.objectTypes.find((type) => type.name === subset.objectType);\n if (!definition)\n return yield* new BackendError({\n operation: \"foundry.loadSubset\",\n kind: \"InvalidRequest\",\n message: \"Unknown Foundry object type.\",\n retryable: false,\n });\n const predicate = subset.query?.where;\n const candidates =\n predicate?.op === \"in\" &&\n isField(predicate.left) &&\n predicate.left.path.length === 1 &&\n predicate.left.path[0] === definition.primaryKey\n ? predicate.values\n : predicate?.op === \"eq\" &&\n isField(predicate.left) &&\n predicate.left.path.length === 1 &&\n predicate.left.path[0] === definition.primaryKey &&\n !isField(predicate.right)\n ? [predicate.right]\n : undefined;\n const keys = candidates?.every(Schema.is(KeySchema))\n ? (candidates as readonly (string | number)[])\n : undefined;\n const plan = analyzeSubsetFilter(predicate);\n const pushed = keys\n ? { type: \"in\" as const, field: definition.primaryKey, value: [...new Set(keys)] }\n : plan.query;\n const values = yield* Effect.tryPromise({\n try: (signal) =>\n searchFoundryObjects(\n options.client,\n subset.objectType,\n definition.properties.map((property) => property.name),\n {\n signal,\n where: pushed,\n },\n (value) => options.decodeObject(subset.objectType, value)\n ),\n catch: unavailable,\n });\n const rows = yield* Effect.forEach(values, (value) =>\n Schema.decodeUnknownEffect(KeySchema)(value[definition.primaryKey]).pipe(\n Effect.map((key) => ({ key, value })),\n Effect.mapError((cause) => invalidResponse(\"foundry.loadSubset\", cause))\n )\n );\n return {\n fulfilledSubset: objectSubset(subset.objectType, { where: subset.query?.where }),\n fetchedRows: values.length,\n pushdown: {\n source: \"Foundry\",\n predicates: plan.predicates.map((predicate) => ({\n ...predicate,\n execution:\n predicate.execution === \"adapter\" ? (\"engine\" as const) : predicate.execution,\n })),\n sorting: subset.query?.orderBy?.length ? \"engine\" : \"none\",\n pagination:\n subset.query?.limit !== undefined ||\n subset.query?.offset !== undefined ||\n subset.query?.after\n ? \"engine\"\n : \"none\",\n rechecksFilter: Boolean(predicate),\n native: {\n language: \"json\",\n text: JSON.stringify(pushed ?? { type: \"and\", value: [] }, null, 2),\n },\n } satisfies SourceQueryPlan,\n rows,\n complete: true,\n };\n }),\n applyAction: Effect.fn(\"Foundry.applyAction\")(function* (invocation: ActionRequest) {\n const { result: rawResult, attachmentIdMappings } = yield* Effect.tryPromise({\n try: (signal) => options.submitAction(invocation, signal),\n catch: (cause) => classifyBackendFailure(\"foundry.applyAction\", cause),\n });\n const result = yield* Schema.decodeUnknownEffect(ActionResult)(rawResult).pipe(\n Effect.mapError((cause) => invalidResponse(\"foundry.applyAction\", cause))\n );\n return yield* Effect.try({\n try: () => {\n const targets = new Map<string, Set<string | number> | undefined>();\n const add = (objectType: string, rawKey?: unknown) => {\n const definition = options.ir.objectTypes.find((type) => type.name === objectType);\n if (!definition || objectType === options.users?.objectType) return;\n const key =\n rawKey === undefined\n ? undefined\n : options.decodeObject(objectType, { [definition.primaryKey]: rawKey })[\n definition.primaryKey\n ];\n if (rawKey === undefined) targets.set(objectType, undefined);\n else if (!Schema.is(KeySchema)(key)) throw new Error(\"Invalid edit identity.\");\n else if (!targets.has(objectType) || targets.get(objectType))\n targets.set(objectType, new Set([...(targets.get(objectType) ?? []), key]));\n };\n if (!result.edits) for (const type of options.ir.objectTypes) add(type.name);\n else if (result.edits.type === \"largeScaleEdits\")\n for (const type of result.edits.editedObjectTypes) add(type);\n else\n for (const edit of result.edits.edits) {\n if (\"aSideObject\" in edit) {\n add(edit.aSideObject.objectType, edit.aSideObject.primaryKey);\n add(edit.bSideObject.objectType, edit.bSideObject.primaryKey);\n } else add(edit.objectType, edit.primaryKey);\n }\n return {\n operationId: invocation.id,\n outcome: \"committed\",\n providerOperationId: result.operationId,\n affected: [...targets].map(([objectType, keys]) => ({\n objectType,\n ...(keys ? { keys: [...keys] } : {}),\n })),\n attachmentIdMappings: attachmentIdMappings.length ? attachmentIdMappings : undefined,\n } satisfies ActionReceipt;\n },\n catch: (cause) => invalidResponse(\"foundry.applyAction\", cause),\n });\n }),\n // Admin users have no ontology websocket feed; the Replica owns this polling lifetime.\n refresh: (subset: ObjectSubset) =>\n subset.from.source.objectType === options.users?.objectType\n ? { pollIntervalMs: 60_000 }\n : undefined,\n watchSubset: watcher\n ? (request: ObjectSubset) =>\n request.from.source.objectType === options.users?.objectType\n ? undefined\n : Effect.gen(function* () {\n const subset = yield* compileObjectSubset(options.ir, request, options.context);\n const filter = subsetFilter(subset.query?.where);\n return yield* acquireWatch(\n watcher.subscribe(\n filter\n ? {\n type: \"filter\",\n objectSet: { type: \"base\", objectType: subset.objectType },\n where: filter,\n }\n : { type: \"base\", objectType: subset.objectType }\n )\n );\n })\n : undefined,\n });\n});\n"],"mappings":";;;;;;;;;AAwBA,MAAM,YAAY,OAAO,MAAM,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC;AAC7D,MAAM,eAAe,OAAO,OAAO;CAAE,YAAY,OAAO;CAAQ,YAAY,OAAO;AAAQ,CAAC;AAC5F,MAAM,eAAe,OAAO,OAAO;CAC/B,aAAa,OAAO,SAAS,OAAO,MAAM;CAC1C,OAAO,OAAO,SACV,OAAO,MAAM,CACT,OAAO,OAAO;EACV,MAAM,OAAO,QAAQ,iBAAiB;EACtC,mBAAmB,OAAO,MAAM,OAAO,MAAM;CACjD,CAAC,GACD,OAAO,OAAO;EACV,MAAM,OAAO,QAAQ,OAAO;EAC5B,OAAO,OAAO,MACV,OAAO,MAAM,CACT,OAAO,OAAO;GACV,MAAM,OAAO,SAAS;IAAC;IAAa;IAAgB;GAAc,CAAC;GACnE,GAAG,aAAa;EACpB,CAAC,GACD,OAAO,OAAO;GACV,MAAM,OAAO,SAAS,CAAC,WAAW,YAAY,CAAC;GAC/C,aAAa;GACb,aAAa;EACjB,CAAC,CACL,CAAC,CACL;CACJ,CAAC,CACL,CAAC,CACL;AACJ,CAAC;AACD,MAAM,mBAAmB,WAAmB,UACxC,IAAI,aAAa;CACb;CACA,MAAM;CACN,SAAS;CACT,WAAW;CACX,eAAe;CACf,OAAO,SAAS,KAAK,KAAK;AAC9B,CAAC;AACL,MAAM,eAAe,UAAmB,uBAAuB,gBAAgB,KAAK;;;;AAKpF,MAAM,eAAe,OAAO,GAAG,sBAAsB,CAAC,CAAC,WACnD,UACF;CACE,MAAM,QAAQ,OAAO,SAAS,KAAyB;CACvD,MAAM,SAAS,OAAO,OAAO,eAAe,MAAM,QAAwC,CAAC,IAAI,UAC3F,MAAM,SAAS,KAAK,CAAC,CAAC,KAAK,OAAO,MAAM,CAC5C;CACA,MAAM,oBACF,IAAI,aAAa;EACb,WAAW;EACX,MAAM;EACN,WAAW;EACX,SAAS;CACb,CAAC;CACL,MAAM,UAAU,OAAO,WAAW,WAAW,YAAY;EACrD,IAAI,QAAQ,SAAS,SAAS,OAAO,MAAM,MAAM,QAAQ,KAAA,CAAS,CAAC,CAAC,KAAK,OAAO,MAAM;EACtF,OAAO,QAAQ,WAAW,SACpB,SAAS,QAAQ,OAAO,KAAA,CAAS,CAAC,CAAC,KAAK,OAAO,MAAM,IACrD,OAAO,KAAK,YAAY,CAAC;CACnC,CAAC,CAAC,CAAC,KACC,OAAO,SAAS,WAAW,GAC3B,OAAO,QAAQ,OAAO,KAAK,YAAY,CAAC,CAAC,GACzC,OAAO,YAAY,UACf,SAAS,UAAU,OAAO,KAAK,CAAC,CAAC,KAC7B,OAAO,QAAQ,MAAM,UAAU,QAAQ,KAAK,CAAC,GAC7C,OAAO,MACX,CACJ,CACJ;CACA,OAAO,OAAO,WAAW,SAAS,EAAE,kBAAkB,KAAK,CAAC;CAC5D,OAAO,SAAS,MAAM,KAAK;CAC3B,OAAO,OAAO,UAAU,MAAM;AAClC,CAAC;;;;AAKD,MAAa,qBAAqB,OAAO,GAAG,qBAAqB,CAAC,CAAC,WAAW,SAwB3E;CACC,MAAM,UACF,QAAQ,UAAU,QACZ,KAAA,IACA,OAAO,qBAAqB,QAAQ,MAAM,CAAC,CAAC,KAAK,OAAO,SAAS,WAAW,CAAC;CACvF,OAAO,oBAAoB;EACvB,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB,MAAM;EACN,UAAU,WAAW,QAAQ,OAAO,QAAQ,GAAG,QAAQ,OAAO;EAC9D,qBAAqB;EACrB,gBAAgB,MAAc,eAC1B,QAAQ,gBACF,OAAO,WAAW;GACd,MAAM,WAAW,QAAQ,cAAe,MAAM,YAAY,MAAM;GAChE,OAAO;EACX,CAAC,IACD,OAAO,KACH,IAAI,aAAa;GACb,WAAW;GACX,MAAM;GACN,SAAS;GACT,WAAW;EACf,CAAC,CACL;EACV,aAAa,QAAQ;EACrB,WAAW,QAAQ;EACnB,YAAY,OAAO,GAAG,oBAAoB,CAAC,CAAC,WAAW,SAAuB;GAC1E,MAAM,SAAS,OAAO,oBAAoB,QAAQ,IAAI,SAAS,QAAQ,OAAO;GAC9E,OAAO,OAAO,oBAAoB,cAAc,OAAO,UAAU;GACjE,OAAO,OAAO,oBAAoB,WAAW,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,KAC/D,OAAO,UACF,UACG,IAAI,aAAa;IACb,WAAW;IACX,MAAM;IACN,SAAS;IACT,WAAW;IACX,OAAO,SAAS,KAAK,KAAK;GAC9B,CAAC,CACT,CACJ;GACA,IAAI,OAAO,eAAe,QAAQ,OAAO,YACrC,OAAO,OAAO,OAAO,WAAW;IAC5B,MAAM,WAAW,QAAQ,MAAO,WAAW,OAAO,SAAS,CAAC,GAAG,MAAM;IACrE,OAAO;GACX,CAAC;GACL,MAAM,aAAa,QAAQ,GAAG,YAAY,MAAM,SAAS,KAAK,SAAS,OAAO,UAAU;GACxF,IAAI,CAAC,YACD,OAAO,OAAO,IAAI,aAAa;IAC3B,WAAW;IACX,MAAM;IACN,SAAS;IACT,WAAW;GACf,CAAC;GACL,MAAM,YAAY,OAAO,OAAO;GAChC,MAAM,aACF,WAAW,OAAO,QAClB,QAAQ,UAAU,IAAI,KACtB,UAAU,KAAK,KAAK,WAAW,KAC/B,UAAU,KAAK,KAAK,OAAO,WAAW,aAChC,UAAU,SACV,WAAW,OAAO,QAChB,QAAQ,UAAU,IAAI,KACtB,UAAU,KAAK,KAAK,WAAW,KAC/B,UAAU,KAAK,KAAK,OAAO,WAAW,cACtC,CAAC,QAAQ,UAAU,KAAK,IACxB,CAAC,UAAU,KAAK,IAChB,KAAA;GACZ,MAAM,OAAO,YAAY,MAAM,OAAO,GAAG,SAAS,CAAC,IAC5C,aACD,KAAA;GACN,MAAM,OAAO,oBAAoB,SAAS;GAC1C,MAAM,SAAS,OACT;IAAE,MAAM;IAAe,OAAO,WAAW;IAAY,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC;GAAE,IAC/E,KAAK;GACX,MAAM,SAAS,OAAO,OAAO,WAAW;IACpC,MAAM,WACF,qBACI,QAAQ,QACR,OAAO,YACP,WAAW,WAAW,KAAK,aAAa,SAAS,IAAI,GACrD;KACI;KACA,OAAO;IACX,IACC,UAAU,QAAQ,aAAa,OAAO,YAAY,KAAK,CAC5D;IACJ,OAAO;GACX,CAAC;GACD,MAAM,OAAO,OAAO,OAAO,QAAQ,SAAS,UACxC,OAAO,oBAAoB,SAAS,CAAC,CAAC,MAAM,WAAW,WAAW,CAAC,CAAC,KAChE,OAAO,KAAK,SAAS;IAAE;IAAK;GAAM,EAAE,GACpC,OAAO,UAAU,UAAU,gBAAgB,sBAAsB,KAAK,CAAC,CAC3E,CACJ;GACA,OAAO;IACH,iBAAiB,aAAa,OAAO,YAAY,EAAE,OAAO,OAAO,OAAO,MAAM,CAAC;IAC/E,aAAa,OAAO;IACpB,UAAU;KACN,QAAQ;KACR,YAAY,KAAK,WAAW,KAAK,eAAe;MAC5C,GAAG;MACH,WACI,UAAU,cAAc,YAAa,WAAqB,UAAU;KAC5E,EAAE;KACF,SAAS,OAAO,OAAO,SAAS,SAAS,WAAW;KACpD,YACI,OAAO,OAAO,UAAU,KAAA,KACxB,OAAO,OAAO,WAAW,KAAA,KACzB,OAAO,OAAO,QACR,WACA;KACV,gBAAgB,QAAQ,SAAS;KACjC,QAAQ;MACJ,UAAU;MACV,MAAM,KAAK,UAAU,UAAU;OAAE,MAAM;OAAO,OAAO,CAAC;MAAE,GAAG,MAAM,CAAC;KACtE;IACJ;IACA;IACA,UAAU;GACd;EACJ,CAAC;EACD,aAAa,OAAO,GAAG,qBAAqB,CAAC,CAAC,WAAW,YAA2B;GAChF,MAAM,EAAE,QAAQ,WAAW,yBAAyB,OAAO,OAAO,WAAW;IACzE,MAAM,WAAW,QAAQ,aAAa,YAAY,MAAM;IACxD,QAAQ,UAAU,uBAAuB,uBAAuB,KAAK;GACzE,CAAC;GACD,MAAM,SAAS,OAAO,OAAO,oBAAoB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,KACtE,OAAO,UAAU,UAAU,gBAAgB,uBAAuB,KAAK,CAAC,CAC5E;GACA,OAAO,OAAO,OAAO,IAAI;IACrB,WAAW;KACP,MAAM,0BAAU,IAAI,IAA8C;KAClE,MAAM,OAAO,YAAoB,WAAqB;MAClD,MAAM,aAAa,QAAQ,GAAG,YAAY,MAAM,SAAS,KAAK,SAAS,UAAU;MACjF,IAAI,CAAC,cAAc,eAAe,QAAQ,OAAO,YAAY;MAC7D,MAAM,MACF,WAAW,KAAA,IACL,KAAA,IACA,QAAQ,aAAa,YAAY,GAAG,WAAW,aAAa,OAAO,CAAC,CAAC,CACjE,WAAW;MAEzB,IAAI,WAAW,KAAA,GAAW,QAAQ,IAAI,YAAY,KAAA,CAAS;WACtD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,GAAG,GAAG,MAAM,IAAI,MAAM,wBAAwB;WACxE,IAAI,CAAC,QAAQ,IAAI,UAAU,KAAK,QAAQ,IAAI,UAAU,GACvD,QAAQ,IAAI,4BAAY,IAAI,IAAI,CAAC,GAAI,QAAQ,IAAI,UAAU,KAAK,CAAC,GAAI,GAAG,CAAC,CAAC;KAClF;KACA,IAAI,CAAC,OAAO,OAAO,KAAK,MAAM,QAAQ,QAAQ,GAAG,aAAa,IAAI,KAAK,IAAI;UACtE,IAAI,OAAO,MAAM,SAAS,mBAC3B,KAAK,MAAM,QAAQ,OAAO,MAAM,mBAAmB,IAAI,IAAI;UAE3D,KAAK,MAAM,QAAQ,OAAO,MAAM,OAC5B,IAAI,iBAAiB,MAAM;MACvB,IAAI,KAAK,YAAY,YAAY,KAAK,YAAY,UAAU;MAC5D,IAAI,KAAK,YAAY,YAAY,KAAK,YAAY,UAAU;KAChE,OAAO,IAAI,KAAK,YAAY,KAAK,UAAU;KAEnD,OAAO;MACH,aAAa,WAAW;MACxB,SAAS;MACT,qBAAqB,OAAO;MAC5B,UAAU,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,YAAY,WAAW;OAChD;OACA,GAAI,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC;MACtC,EAAE;MACF,sBAAsB,qBAAqB,SAAS,uBAAuB,KAAA;KAC/E;IACJ;IACA,QAAQ,UAAU,gBAAgB,uBAAuB,KAAK;GAClE,CAAC;EACL,CAAC;EAED,UAAU,WACN,OAAO,KAAK,OAAO,eAAe,QAAQ,OAAO,aAC3C,EAAE,gBAAgB,IAAO,IACzB,KAAA;EACV,aAAa,WACN,YACG,QAAQ,KAAK,OAAO,eAAe,QAAQ,OAAO,aAC5C,KAAA,IACA,OAAO,IAAI,aAAa;GACpB,MAAM,SAAS,OAAO,oBAAoB,QAAQ,IAAI,SAAS,QAAQ,OAAO;GAC9E,MAAM,SAAS,aAAa,OAAO,OAAO,KAAK;GAC/C,OAAO,OAAO,aACV,QAAQ,UACJ,SACM;IACI,MAAM;IACN,WAAW;KAAE,MAAM;KAAQ,YAAY,OAAO;IAAW;IACzD,OAAO;GACX,IACA;IAAE,MAAM;IAAQ,YAAY,OAAO;GAAW,CACxD,CACJ;EACJ,CAAC,IACX,KAAA;CACV,CAAC;AACL,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { OntologyClient } from "@party-stack/foundry-client";
2
+ import { BackendEncryptedValues } from "@party-stack/ontology-backend";
3
+ import { OntologyIR } from "@party-stack/ontology-spec";
4
+ import { Encrypted } from "@party-stack/ontology-spec/values";
5
+ //#region src/adapter/encrypted.d.ts
6
+ /** Ciphertext is an opaque identifier here, never implicitly decrypted or interpreted as plaintext. */
7
+ export declare function decodeFoundryCiphertext(value: unknown, source?: Encrypted["source"]): Encrypted;
8
+ export declare const encodeFoundryCiphertext: (value: unknown) => string;
9
+ /** Foundry's property endpoint reads the property's current value; it does not decrypt an arbitrary blob. */
10
+ export declare function makeFoundryEncryptedValues(client: OntologyClient, ir: OntologyIR): BackendEncryptedValues;
11
+ //#endregion
12
+ //# sourceMappingURL=encrypted.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypted.d.ts","names":[],"sources":["../../src/adapter/encrypted.ts"],"mappings":";;;;;;wBAkBgB,wBAAwB,gBAAgB,SAAS,sBAAsB;qBAO1E,0BAAuB;;wBAgCpB,2BAA2B,QAAQ,gBAAgB,IAAI,aAAa"}
@@ -0,0 +1,79 @@
1
+ import { Effect, Predicate, Schema } from "effect";
2
+ import { CipherTextProperties } from "@osdk/foundry.ontologies";
3
+ import { BackendError, encryptedPropertyCodec } from "@party-stack/ontology-backend";
4
+ import { resolveType } from "@party-stack/ontology-spec/utils";
5
+ //#region src/adapter/encrypted.ts
6
+ const Ciphertext = Schema.String.check(Schema.isPattern(/^CIPHER::[^:]+::.+::CIPHER$/u));
7
+ const Source = Schema.Struct({
8
+ objectType: Schema.String,
9
+ primaryKey: Schema.Union([Schema.String, Schema.Finite]),
10
+ property: Schema.String
11
+ });
12
+ const Handle = Schema.Struct({
13
+ ciphertext: Ciphertext,
14
+ source: Schema.optional(Source)
15
+ });
16
+ /** Ciphertext is an opaque identifier here, never implicitly decrypted or interpreted as plaintext. */
17
+ function decodeFoundryCiphertext(value, source) {
18
+ const handle = Predicate.isString(value) ? { ciphertext: Schema.decodeUnknownSync(Ciphertext)(value) } : Schema.decodeUnknownSync(Handle)(value);
19
+ return source ? {
20
+ ...handle,
21
+ source
22
+ } : handle;
23
+ }
24
+ const encodeFoundryCiphertext = (value) => Schema.decodeUnknownSync(Handle)(value).ciphertext;
25
+ const rejected = (operation, message, kind = "InvalidRequest") => new BackendError({
26
+ operation,
27
+ kind,
28
+ message,
29
+ retryable: false
30
+ });
31
+ function requestError(operation, cause) {
32
+ const status = Predicate.isObject(cause) ? cause.statusCode : void 0;
33
+ return new BackendError({
34
+ operation,
35
+ kind: status === 404 ? "NotFound" : status === 401 ? "Unauthenticated" : status === 403 ? "Forbidden" : status === 429 ? "RateLimited" : status === 400 ? "InvalidRequest" : status === 200 ? "InvalidResponse" : "Unavailable",
36
+ message: "Foundry CipherText request failed.",
37
+ retryable: status === void 0 || status === 429 || Predicate.isNumber(status) && status >= 500
38
+ });
39
+ }
40
+ /** Foundry's property endpoint reads the property's current value; it does not decrypt an arbitrary blob. */
41
+ function makeFoundryEncryptedValues(client, ir) {
42
+ const requestClient = (signal) => ({
43
+ ...client,
44
+ fetch: (input, init) => client.fetch(input, {
45
+ ...init,
46
+ signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal
47
+ })
48
+ });
49
+ return { decrypt: Effect.fn("Foundry.decrypt")(function* (value) {
50
+ const handle = yield* Effect.try({
51
+ try: () => Schema.decodeUnknownSync(Handle)(value),
52
+ catch: () => rejected("foundry.decrypt", "Invalid Foundry encrypted handle.")
53
+ });
54
+ if (!handle.source) return yield* rejected("foundry.decrypt", "Foundry decryption requires an object property source.");
55
+ const codec = yield* Effect.try({
56
+ try: () => {
57
+ const codec = encryptedPropertyCodec(ir, handle);
58
+ if (resolveType(ir, codec.type).kind !== "string") throw new Error();
59
+ return codec;
60
+ },
61
+ catch: () => rejected("foundry.decrypt", "Expected a CipherText property with string plaintext.")
62
+ });
63
+ const result = yield* Effect.tryPromise({
64
+ try: (signal) => CipherTextProperties.decrypt(requestClient(signal), client.ontologyRid, handle.source.objectType, String(handle.source.primaryKey), handle.source.property),
65
+ catch: (cause) => requestError("foundry.decrypt", cause)
66
+ });
67
+ return yield* Effect.try({
68
+ try: () => ({
69
+ value: codec.encode(result.plaintext),
70
+ ciphertext: handle.ciphertext
71
+ }),
72
+ catch: () => rejected("foundry.decrypt", "Invalid decrypted plaintext.", "InvalidResponse")
73
+ });
74
+ }) };
75
+ }
76
+ //#endregion
77
+ export { decodeFoundryCiphertext, encodeFoundryCiphertext, makeFoundryEncryptedValues };
78
+
79
+ //# sourceMappingURL=encrypted.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypted.js","names":[],"sources":["../../src/adapter/encrypted.ts"],"sourcesContent":["import { CipherTextProperties } from \"@osdk/foundry.ontologies\";\nimport { BackendError, encryptedPropertyCodec } from \"@party-stack/ontology-backend\";\nimport { resolveType } from \"@party-stack/ontology-spec/utils\";\nimport { Effect, Predicate, Schema } from \"effect\";\nimport type { OntologyClient } from \"@party-stack/foundry-client\";\nimport type { BackendEncryptedValues } from \"@party-stack/ontology-backend\";\nimport type { OntologyIR } from \"@party-stack/ontology-spec\";\nimport type { Encrypted } from \"@party-stack/ontology-spec/values\";\n\nconst Ciphertext = Schema.String.check(Schema.isPattern(/^CIPHER::[^:]+::.+::CIPHER$/u));\nconst Source = Schema.Struct({\n objectType: Schema.String,\n primaryKey: Schema.Union([Schema.String, Schema.Finite]),\n property: Schema.String,\n});\nconst Handle = Schema.Struct({ ciphertext: Ciphertext, source: Schema.optional(Source) });\n\n/** Ciphertext is an opaque identifier here, never implicitly decrypted or interpreted as plaintext. */\nexport function decodeFoundryCiphertext(value: unknown, source?: Encrypted[\"source\"]): Encrypted {\n const handle = Predicate.isString(value)\n ? { ciphertext: Schema.decodeUnknownSync(Ciphertext)(value) }\n : Schema.decodeUnknownSync(Handle)(value);\n return source ? { ...handle, source } : handle;\n}\n\nexport const encodeFoundryCiphertext = (value: unknown): string =>\n Schema.decodeUnknownSync(Handle)(value).ciphertext;\n\nconst rejected = (operation: string, message: string, kind: BackendError[\"kind\"] = \"InvalidRequest\") =>\n new BackendError({ operation, kind, message, retryable: false });\n\nfunction requestError(operation: string, cause: unknown): BackendError {\n const status = Predicate.isObject(cause) ? cause.statusCode : undefined;\n // Provider errors and decoder issues may contain sensitive bodies. Preserve the\n // classification, without attaching raw requests, ciphertext or plaintext to traces.\n return new BackendError({\n operation,\n kind:\n status === 404\n ? \"NotFound\"\n : status === 401\n ? \"Unauthenticated\"\n : status === 403\n ? \"Forbidden\"\n : status === 429\n ? \"RateLimited\"\n : status === 400\n ? \"InvalidRequest\"\n : status === 200\n ? \"InvalidResponse\"\n : \"Unavailable\",\n message: \"Foundry CipherText request failed.\",\n retryable: status === undefined || status === 429 || (Predicate.isNumber(status) && status >= 500),\n });\n}\n\n/** Foundry's property endpoint reads the property's current value; it does not decrypt an arbitrary blob. */\nexport function makeFoundryEncryptedValues(client: OntologyClient, ir: OntologyIR): BackendEncryptedValues {\n const requestClient = (signal: AbortSignal): OntologyClient => ({\n ...client,\n fetch: (input, init) =>\n client.fetch(input, {\n ...init,\n signal: init?.signal ? AbortSignal.any([signal, init.signal]) : signal,\n }),\n });\n return {\n decrypt: Effect.fn(\"Foundry.decrypt\")(function* (value) {\n const handle = yield* Effect.try({\n try: () => Schema.decodeUnknownSync(Handle)(value),\n catch: () => rejected(\"foundry.decrypt\", \"Invalid Foundry encrypted handle.\"),\n });\n if (!handle.source)\n return yield* rejected(\n \"foundry.decrypt\",\n \"Foundry decryption requires an object property source.\"\n );\n const codec = yield* Effect.try({\n try: () => {\n const codec = encryptedPropertyCodec(ir, handle);\n if (resolveType(ir, codec.type).kind !== \"string\") throw new Error();\n return codec;\n },\n catch: () =>\n rejected(\"foundry.decrypt\", \"Expected a CipherText property with string plaintext.\"),\n });\n const result = yield* Effect.tryPromise({\n try: (signal) =>\n CipherTextProperties.decrypt(\n requestClient(signal),\n client.ontologyRid,\n handle.source!.objectType,\n String(handle.source!.primaryKey),\n handle.source!.property\n ),\n catch: (cause) => requestError(\"foundry.decrypt\", cause),\n });\n return yield* Effect.try({\n // TODO(Foundry FR): require the decrypt response to echo its ciphertext.\n // Until then, assume the property has not changed since this handle was read.\n try: () => ({ value: codec.encode(result.plaintext), ciphertext: handle.ciphertext }),\n catch: () => rejected(\"foundry.decrypt\", \"Invalid decrypted plaintext.\", \"InvalidResponse\"),\n });\n }),\n };\n}\n"],"mappings":";;;;;AASA,MAAM,aAAa,OAAO,OAAO,MAAM,OAAO,UAAU,8BAA8B,CAAC;AACvF,MAAM,SAAS,OAAO,OAAO;CACzB,YAAY,OAAO;CACnB,YAAY,OAAO,MAAM,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC;CACvD,UAAU,OAAO;AACrB,CAAC;AACD,MAAM,SAAS,OAAO,OAAO;CAAE,YAAY;CAAY,QAAQ,OAAO,SAAS,MAAM;AAAE,CAAC;;AAGxF,SAAgB,wBAAwB,OAAgB,QAAyC;CAC7F,MAAM,SAAS,UAAU,SAAS,KAAK,IACjC,EAAE,YAAY,OAAO,kBAAkB,UAAU,CAAC,CAAC,KAAK,EAAE,IAC1D,OAAO,kBAAkB,MAAM,CAAC,CAAC,KAAK;CAC5C,OAAO,SAAS;EAAE,GAAG;EAAQ;CAAO,IAAI;AAC5C;AAEA,MAAa,2BAA2B,UACpC,OAAO,kBAAkB,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;AAE5C,MAAM,YAAY,WAAmB,SAAiB,OAA6B,qBAC/E,IAAI,aAAa;CAAE;CAAW;CAAM;CAAS,WAAW;AAAM,CAAC;AAEnE,SAAS,aAAa,WAAmB,OAA8B;CACnE,MAAM,SAAS,UAAU,SAAS,KAAK,IAAI,MAAM,aAAa,KAAA;CAG9D,OAAO,IAAI,aAAa;EACpB;EACA,MACI,WAAW,MACL,aACA,WAAW,MACT,oBACA,WAAW,MACT,cACA,WAAW,MACT,gBACA,WAAW,MACT,mBACA,WAAW,MACT,oBACA;EACpB,SAAS;EACT,WAAW,WAAW,KAAA,KAAa,WAAW,OAAQ,UAAU,SAAS,MAAM,KAAK,UAAU;CAClG,CAAC;AACL;;AAGA,SAAgB,2BAA2B,QAAwB,IAAwC;CACvG,MAAM,iBAAiB,YAAyC;EAC5D,GAAG;EACH,QAAQ,OAAO,SACX,OAAO,MAAM,OAAO;GAChB,GAAG;GACH,QAAQ,MAAM,SAAS,YAAY,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI;EACpE,CAAC;CACT;CACA,OAAO,EACH,SAAS,OAAO,GAAG,iBAAiB,CAAC,CAAC,WAAW,OAAO;EACpD,MAAM,SAAS,OAAO,OAAO,IAAI;GAC7B,WAAW,OAAO,kBAAkB,MAAM,CAAC,CAAC,KAAK;GACjD,aAAa,SAAS,mBAAmB,mCAAmC;EAChF,CAAC;EACD,IAAI,CAAC,OAAO,QACR,OAAO,OAAO,SACV,mBACA,wDACJ;EACJ,MAAM,QAAQ,OAAO,OAAO,IAAI;GAC5B,WAAW;IACP,MAAM,QAAQ,uBAAuB,IAAI,MAAM;IAC/C,IAAI,YAAY,IAAI,MAAM,IAAI,CAAC,CAAC,SAAS,UAAU,MAAM,IAAI,MAAM;IACnE,OAAO;GACX;GACA,aACI,SAAS,mBAAmB,uDAAuD;EAC3F,CAAC;EACD,MAAM,SAAS,OAAO,OAAO,WAAW;GACpC,MAAM,WACF,qBAAqB,QACjB,cAAc,MAAM,GACpB,OAAO,aACP,OAAO,OAAQ,YACf,OAAO,OAAO,OAAQ,UAAU,GAChC,OAAO,OAAQ,QACnB;GACJ,QAAQ,UAAU,aAAa,mBAAmB,KAAK;EAC3D,CAAC;EACD,OAAO,OAAO,OAAO,IAAI;GAGrB,YAAY;IAAE,OAAO,MAAM,OAAO,OAAO,SAAS;IAAG,YAAY,OAAO;GAAW;GACnF,aAAa,SAAS,mBAAmB,gCAAgC,iBAAiB;EAC9F,CAAC;CACL,CAAC,EACL;AACJ"}
@@ -0,0 +1,7 @@
1
+ import { BackendError } from "@party-stack/ontology-backend";
2
+ import { OntologyIR } from "@party-stack/ontology-spec";
3
+ //#region src/adapter/foundryActionError.d.ts
4
+ /** Map only provider evidence about submitted references; a general 404 cannot identify a field. */
5
+ export declare function foundryActionError(operation: string, cause: unknown, ir: OntologyIR, actionName: string, values: Record<string, unknown>): BackendError;
6
+ //#endregion
7
+ //# sourceMappingURL=foundryActionError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundryActionError.d.ts","names":[],"sources":["../../src/adapter/foundryActionError.ts"],"mappings":";;;;wBAagB,mBACZ,mBACA,gBACA,IAAI,YACJ,oBACA,QAAQ,0BACT"}
@@ -0,0 +1,61 @@
1
+ import { Schema } from "effect";
2
+ import { BackendError, classifyBackendFailure } from "@party-stack/ontology-backend";
3
+ import { resolveType } from "@party-stack/ontology-spec/utils";
4
+ //#region src/adapter/foundryActionError.ts
5
+ const Failure = Schema.Struct({
6
+ errorName: Schema.optional(Schema.String),
7
+ parameters: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
8
+ cause: Schema.optional(Schema.Unknown),
9
+ reason: Schema.optional(Schema.Unknown)
10
+ });
11
+ /** Map only provider evidence about submitted references; a general 404 cannot identify a field. */
12
+ function foundryActionError(operation, cause, ir, actionName, values) {
13
+ if (cause instanceof BackendError) return cause;
14
+ const normalized = classifyBackendFailure(operation, cause);
15
+ const action = ir.actionTypes.find((action) => action.name === actionName);
16
+ if (!action) return normalized;
17
+ let candidate = cause;
18
+ for (let depth = 0; depth < 5; depth++) {
19
+ const decoded = Schema.decodeUnknownOption(Failure)(candidate);
20
+ if (decoded._tag === "None") break;
21
+ const error = decoded.value;
22
+ const name = error.errorName?.split(":").at(-1);
23
+ const paths = [];
24
+ if (name === "ActionParameterObjectNotFound") {
25
+ const parameter = action.parameters.find((parameter) => parameter.name === error.parameters?.parameterId);
26
+ if (parameter) paths.push([parameter.name]);
27
+ } else if (name === "ParameterObjectNotFound" || name === "ObjectNotFound") {
28
+ const visit = (definition, value, path) => {
29
+ if (value === void 0 || value === null) return;
30
+ const type = resolveType(ir, definition);
31
+ if (type.kind === "optional") visit(type.value.type, value, path);
32
+ else if (type.kind === "list" && Array.isArray(value)) value.forEach((item, index) => visit(type.value.elementType, item, [...path, index]));
33
+ else if (type.kind === "objectReference" && type.value.objectType === error.parameters?.objectType && Object.is(value, error.parameters?.primaryKey)) paths.push(path);
34
+ };
35
+ for (const parameter of action.parameters) visit(parameter.type, values[parameter.name], [parameter.name]);
36
+ }
37
+ if (paths.length) return new BackendError({
38
+ operation,
39
+ kind: "ValidationFailed",
40
+ message: "Submission failed validation.",
41
+ retryable: false,
42
+ actionOutcome: "not-committed",
43
+ cause: normalized.cause,
44
+ issues: paths.map((path) => ({
45
+ path,
46
+ code: "InvalidObjectReference",
47
+ category: "reference",
48
+ source: "parameter",
49
+ messageSource: "standard",
50
+ message: "Choose a valid value."
51
+ }))
52
+ });
53
+ if (name) break;
54
+ candidate = error.reason ?? error.cause;
55
+ }
56
+ return normalized;
57
+ }
58
+ //#endregion
59
+ export { foundryActionError };
60
+
61
+ //# sourceMappingURL=foundryActionError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundryActionError.js","names":[],"sources":["../../src/adapter/foundryActionError.ts"],"sourcesContent":["import { BackendError, classifyBackendFailure } from \"@party-stack/ontology-backend\";\nimport { resolveType } from \"@party-stack/ontology-spec/utils\";\nimport { Schema } from \"effect\";\nimport type { OntologyIR, TypeDef } from \"@party-stack/ontology-spec\";\n\nconst Failure = Schema.Struct({\n errorName: Schema.optional(Schema.String),\n parameters: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n cause: Schema.optional(Schema.Unknown),\n reason: Schema.optional(Schema.Unknown),\n});\n\n/** Map only provider evidence about submitted references; a general 404 cannot identify a field. */\nexport function foundryActionError(\n operation: string,\n cause: unknown,\n ir: OntologyIR,\n actionName: string,\n values: Record<string, unknown>\n): BackendError {\n if (cause instanceof BackendError) return cause;\n const normalized = classifyBackendFailure(operation, cause);\n const action = ir.actionTypes.find((action) => action.name === actionName);\n if (!action) return normalized;\n let candidate = cause;\n for (let depth = 0; depth < 5; depth++) {\n const decoded = Schema.decodeUnknownOption(Failure)(candidate);\n if (decoded._tag === \"None\") break;\n const error = decoded.value;\n const name = error.errorName?.split(\":\").at(-1);\n const paths: Array<readonly (string | number)[]> = [];\n if (name === \"ActionParameterObjectNotFound\") {\n const parameter = action.parameters.find(\n (parameter) => parameter.name === error.parameters?.parameterId\n );\n if (parameter) paths.push([parameter.name]);\n } else if (name === \"ParameterObjectNotFound\" || name === \"ObjectNotFound\") {\n const visit = (definition: TypeDef, value: unknown, path: readonly (string | number)[]) => {\n if (value === undefined || value === null) return;\n const type = resolveType(ir, definition);\n if (type.kind === \"optional\") visit(type.value.type, value, path);\n else if (type.kind === \"list\" && Array.isArray(value))\n value.forEach((item, index) => visit(type.value.elementType, item, [...path, index]));\n else if (\n type.kind === \"objectReference\" &&\n type.value.objectType === error.parameters?.objectType &&\n Object.is(value, error.parameters?.primaryKey)\n )\n paths.push(path);\n };\n for (const parameter of action.parameters)\n visit(parameter.type, values[parameter.name], [parameter.name]);\n }\n if (paths.length)\n return new BackendError({\n operation,\n kind: \"ValidationFailed\",\n message: \"Submission failed validation.\",\n retryable: false,\n actionOutcome: \"not-committed\",\n cause: normalized.cause,\n issues: paths.map((path) => ({\n path,\n code: \"InvalidObjectReference\",\n category: \"reference\",\n source: \"parameter\",\n messageSource: \"standard\",\n message: \"Choose a valid value.\",\n })),\n });\n if (name) break;\n candidate = error.reason ?? error.cause;\n }\n return normalized;\n}\n"],"mappings":";;;;AAKA,MAAM,UAAU,OAAO,OAAO;CAC1B,WAAW,OAAO,SAAS,OAAO,MAAM;CACxC,YAAY,OAAO,SAAS,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC;CACxE,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,QAAQ,OAAO,SAAS,OAAO,OAAO;AAC1C,CAAC;;AAGD,SAAgB,mBACZ,WACA,OACA,IACA,YACA,QACY;CACZ,IAAI,iBAAiB,cAAc,OAAO;CAC1C,MAAM,aAAa,uBAAuB,WAAW,KAAK;CAC1D,MAAM,SAAS,GAAG,YAAY,MAAM,WAAW,OAAO,SAAS,UAAU;CACzE,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,YAAY;CAChB,KAAK,IAAI,QAAQ,GAAG,QAAQ,GAAG,SAAS;EACpC,MAAM,UAAU,OAAO,oBAAoB,OAAO,CAAC,CAAC,SAAS;EAC7D,IAAI,QAAQ,SAAS,QAAQ;EAC7B,MAAM,QAAQ,QAAQ;EACtB,MAAM,OAAO,MAAM,WAAW,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE;EAC9C,MAAM,QAA6C,CAAC;EACpD,IAAI,SAAS,iCAAiC;GAC1C,MAAM,YAAY,OAAO,WAAW,MAC/B,cAAc,UAAU,SAAS,MAAM,YAAY,WACxD;GACA,IAAI,WAAW,MAAM,KAAK,CAAC,UAAU,IAAI,CAAC;EAC9C,OAAO,IAAI,SAAS,6BAA6B,SAAS,kBAAkB;GACxE,MAAM,SAAS,YAAqB,OAAgB,SAAuC;IACvF,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;IAC3C,MAAM,OAAO,YAAY,IAAI,UAAU;IACvC,IAAI,KAAK,SAAS,YAAY,MAAM,KAAK,MAAM,MAAM,OAAO,IAAI;SAC3D,IAAI,KAAK,SAAS,UAAU,MAAM,QAAQ,KAAK,GAChD,MAAM,SAAS,MAAM,UAAU,MAAM,KAAK,MAAM,aAAa,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,CAAC;SACnF,IACD,KAAK,SAAS,qBACd,KAAK,MAAM,eAAe,MAAM,YAAY,cAC5C,OAAO,GAAG,OAAO,MAAM,YAAY,UAAU,GAE7C,MAAM,KAAK,IAAI;GACvB;GACA,KAAK,MAAM,aAAa,OAAO,YAC3B,MAAM,UAAU,MAAM,OAAO,UAAU,OAAO,CAAC,UAAU,IAAI,CAAC;EACtE;EACA,IAAI,MAAM,QACN,OAAO,IAAI,aAAa;GACpB;GACA,MAAM;GACN,SAAS;GACT,WAAW;GACX,eAAe;GACf,OAAO,WAAW;GAClB,QAAQ,MAAM,KAAK,UAAU;IACzB;IACA,MAAM;IACN,UAAU;IACV,QAAQ;IACR,eAAe;IACf,SAAS;GACb,EAAE;EACN,CAAC;EACL,IAAI,MAAM;EACV,YAAY,MAAM,UAAU,MAAM;CACtC;CACA,OAAO;AACX"}
@@ -1,8 +1,9 @@
1
1
  import { OntologyClient } from "@party-stack/foundry-client";
2
2
  import { SyncApplyActionResponseV2 } from "@osdk/foundry.ontologies";
3
- import { Uncertain, ValidationIssue } from "@party-stack/ontology";
3
+ import { ValidationIssue } from "@party-stack/ontology-backend";
4
+ import { Uncertain } from "@party-stack/ontology-spec";
4
5
  import { Condition } from "@osdk/client.unstable";
5
- import { Result } from "@party-stack/ontology/values";
6
+ import { Result } from "@party-stack/ontology-spec/values";
6
7
  //#region src/adapter/foundryActionValidation.d.ts
7
8
  export interface FoundrySubmissionCriterion {
8
9
  condition: Condition;
@@ -20,6 +21,6 @@ export declare function validateFoundryActionDraftCriteria(options: {
20
21
  parameters: Record<string, unknown>;
21
22
  knownParameters?: ReadonlySet<string>;
22
23
  }): Promise<Uncertain<Result<void, readonly ValidationIssue[]>>>;
23
- export declare function getFoundryValidationIssues(result: SyncApplyActionResponseV2): ValidationIssue[];
24
+ export declare function getFoundryValidationIssues(result: SyncApplyActionResponseV2, parameters?: Record<string, unknown>): ValidationIssue[];
24
25
  //#endregion
25
26
  //# sourceMappingURL=foundryActionValidation.d.ts.map