@palantir/pack.state.foundry 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-transpileBrowser.log +1 -1
- package/.turbo/turbo-transpileCjs.log +1 -1
- package/.turbo/turbo-transpileEsm.log +1 -1
- package/.turbo/turbo-transpileTypes.log +1 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +13 -0
- package/build/browser/index.js +50 -57
- package/build/browser/index.js.map +1 -1
- package/build/cjs/index.cjs +50 -57
- package/build/cjs/index.cjs.map +1 -1
- package/build/esm/index.js +50 -57
- package/build/esm/index.js.map +1 -1
- package/build/types/FoundryDocumentService.d.ts.map +1 -1
- package/build/types/__tests__/eventMappers.test.d.ts +1 -0
- package/build/types/__tests__/eventMappers.test.d.ts.map +1 -0
- package/build/types/eventMappers.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/FoundryDocumentService.ts +4 -0
- package/src/__tests__/FoundrySecurityConversion.test.ts +1 -0
- package/src/__tests__/FoundryStatusTracking.test.ts +1 -0
- package/src/__tests__/eventMappers.test.ts +258 -0
- package/src/eventMappers.ts +47 -105
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Palantir Technologies, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ActivityCollaborativeUpdate } from "@osdk/foundry.pack";
|
|
18
|
+
import type { DocumentSchema, Model } from "@palantir/pack.document-schema.model-types";
|
|
19
|
+
import { ActivityEventDataType, Metadata } from "@palantir/pack.document-schema.model-types";
|
|
20
|
+
import { describe, expect, it } from "vitest";
|
|
21
|
+
import { getActivityEvent } from "../eventMappers.js";
|
|
22
|
+
|
|
23
|
+
const mockModel = {
|
|
24
|
+
__type: {} as { someField: string },
|
|
25
|
+
zodSchema: {} as Model["zodSchema"],
|
|
26
|
+
[Metadata]: { name: "TestModel" },
|
|
27
|
+
} as unknown as Model;
|
|
28
|
+
|
|
29
|
+
const emptySchema = {
|
|
30
|
+
[Metadata]: { name: "TestSchema", version: 0, models: {} },
|
|
31
|
+
} as unknown as DocumentSchema;
|
|
32
|
+
|
|
33
|
+
const schemaWithModel = {
|
|
34
|
+
...emptySchema,
|
|
35
|
+
TestModel: mockModel,
|
|
36
|
+
} as unknown as DocumentSchema;
|
|
37
|
+
|
|
38
|
+
function makeActivityUpdate(
|
|
39
|
+
eventData: unknown,
|
|
40
|
+
): ActivityCollaborativeUpdate {
|
|
41
|
+
return {
|
|
42
|
+
type: "activityCreated",
|
|
43
|
+
activityEvent: {
|
|
44
|
+
eventId: "event-1",
|
|
45
|
+
eventData,
|
|
46
|
+
isRead: false,
|
|
47
|
+
aggregationKey: "agg-1",
|
|
48
|
+
createdBy: "user-1",
|
|
49
|
+
createdTime: "2026-01-01T00:00:00Z",
|
|
50
|
+
},
|
|
51
|
+
} as ActivityCollaborativeUpdate;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("getActivityEvent", () => {
|
|
55
|
+
it("returns undefined for activityDeleted events", () => {
|
|
56
|
+
const update = {
|
|
57
|
+
type: "activityDeleted",
|
|
58
|
+
eventId: "event-1",
|
|
59
|
+
aggregationKey: "agg-1",
|
|
60
|
+
} as ActivityCollaborativeUpdate;
|
|
61
|
+
|
|
62
|
+
expect(getActivityEvent(emptySchema, update)).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("maps documentCreate events", () => {
|
|
66
|
+
const result = getActivityEvent(
|
|
67
|
+
emptySchema,
|
|
68
|
+
makeActivityUpdate({
|
|
69
|
+
type: "documentCreate",
|
|
70
|
+
name: "My Document",
|
|
71
|
+
initialMandatorySecurity: {
|
|
72
|
+
classification: ["SECRET"],
|
|
73
|
+
markings: ["marking-1"],
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
expect(result).toBeDefined();
|
|
79
|
+
expect(result!.eventData).toEqual({
|
|
80
|
+
type: ActivityEventDataType.DOCUMENT_CREATE,
|
|
81
|
+
name: "My Document",
|
|
82
|
+
initialMandatorySecurity: {
|
|
83
|
+
classification: ["SECRET"],
|
|
84
|
+
markings: ["marking-1"],
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("maps documentCreate with missing fields to defaults", () => {
|
|
90
|
+
const result = getActivityEvent(
|
|
91
|
+
emptySchema,
|
|
92
|
+
makeActivityUpdate({
|
|
93
|
+
type: "documentCreate",
|
|
94
|
+
initialMandatorySecurity: undefined,
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(result!.eventData).toEqual({
|
|
99
|
+
type: ActivityEventDataType.DOCUMENT_CREATE,
|
|
100
|
+
name: "",
|
|
101
|
+
initialMandatorySecurity: {
|
|
102
|
+
classification: undefined,
|
|
103
|
+
markings: undefined,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("maps documentRename events", () => {
|
|
109
|
+
const result = getActivityEvent(
|
|
110
|
+
emptySchema,
|
|
111
|
+
makeActivityUpdate({
|
|
112
|
+
type: "documentRename",
|
|
113
|
+
previousName: "Old Name",
|
|
114
|
+
newName: "New Name",
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(result!.eventData).toEqual({
|
|
119
|
+
type: ActivityEventDataType.DOCUMENT_RENAME,
|
|
120
|
+
previousName: "Old Name",
|
|
121
|
+
newName: "New Name",
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("maps documentDescriptionUpdate events", () => {
|
|
126
|
+
const result = getActivityEvent(
|
|
127
|
+
emptySchema,
|
|
128
|
+
makeActivityUpdate({
|
|
129
|
+
type: "documentDescriptionUpdate",
|
|
130
|
+
newDescription: "Updated description",
|
|
131
|
+
isInitial: false,
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(result!.eventData).toEqual({
|
|
136
|
+
type: ActivityEventDataType.DOCUMENT_DESCRIPTION_UPDATE,
|
|
137
|
+
newDescription: "Updated description",
|
|
138
|
+
isInitial: false,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("maps documentMandatorySecurityUpdate events", () => {
|
|
143
|
+
const result = getActivityEvent(
|
|
144
|
+
emptySchema,
|
|
145
|
+
makeActivityUpdate({
|
|
146
|
+
type: "documentMandatorySecurityUpdate",
|
|
147
|
+
newClassification: ["test-classification"],
|
|
148
|
+
newMarkings: ["test-marking"],
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(result!.eventData).toEqual({
|
|
153
|
+
type: ActivityEventDataType.DOCUMENT_MANDATORY_SECURITY_UPDATE,
|
|
154
|
+
newClassification: ["test-classification"],
|
|
155
|
+
newMarkings: ["test-marking"],
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("maps documentDiscretionarySecurityUpdate events", () => {
|
|
160
|
+
const newSecurity = {
|
|
161
|
+
owners: [{ type: "all" as const }],
|
|
162
|
+
editors: [],
|
|
163
|
+
viewers: [],
|
|
164
|
+
};
|
|
165
|
+
const result = getActivityEvent(
|
|
166
|
+
emptySchema,
|
|
167
|
+
makeActivityUpdate({
|
|
168
|
+
type: "documentDiscretionarySecurityUpdate",
|
|
169
|
+
principalType: "ALL_PRINCIPAL",
|
|
170
|
+
previousDiscretionarySecurity: undefined,
|
|
171
|
+
newDiscretionarySecurity: newSecurity,
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
expect(result!.eventData).toEqual({
|
|
176
|
+
type: ActivityEventDataType.DOCUMENT_DISCRETIONARY_SECURITY_UPDATE,
|
|
177
|
+
principalType: "ALL_PRINCIPAL",
|
|
178
|
+
previousDiscretionarySecurity: undefined,
|
|
179
|
+
newDiscretionarySecurity: newSecurity,
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("maps documentCustomEvent with known model to CUSTOM_EVENT", () => {
|
|
184
|
+
const customData = { eventType: "shapeAdd", nodeId: "node-1" };
|
|
185
|
+
const result = getActivityEvent(
|
|
186
|
+
schemaWithModel,
|
|
187
|
+
makeActivityUpdate({
|
|
188
|
+
type: "documentCustomEvent",
|
|
189
|
+
eventType: "TestModel",
|
|
190
|
+
data: customData,
|
|
191
|
+
version: 1,
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(result!.eventData).toEqual({
|
|
196
|
+
type: ActivityEventDataType.CUSTOM_EVENT,
|
|
197
|
+
model: mockModel,
|
|
198
|
+
eventType: "TestModel",
|
|
199
|
+
data: customData,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("maps documentCustomEvent with unknown model to UNKNOWN", () => {
|
|
204
|
+
const customData = { eventType: "shapeAdd", nodeId: "node-1" };
|
|
205
|
+
const result = getActivityEvent(
|
|
206
|
+
emptySchema,
|
|
207
|
+
makeActivityUpdate({
|
|
208
|
+
type: "documentCustomEvent",
|
|
209
|
+
eventType: "NonExistentModel",
|
|
210
|
+
data: customData,
|
|
211
|
+
version: 1,
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
expect(result!.eventData).toEqual({
|
|
216
|
+
type: ActivityEventDataType.UNKNOWN,
|
|
217
|
+
rawType: "NonExistentModel",
|
|
218
|
+
rawData: customData,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("maps unrecognized event types to UNKNOWN", () => {
|
|
223
|
+
const result = getActivityEvent(
|
|
224
|
+
emptySchema,
|
|
225
|
+
makeActivityUpdate({
|
|
226
|
+
type: "someNewEventType",
|
|
227
|
+
someData: 123,
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
expect(result!.eventData).toEqual({
|
|
232
|
+
type: ActivityEventDataType.UNKNOWN,
|
|
233
|
+
rawType: "someNewEventType",
|
|
234
|
+
rawData: { type: "someNewEventType", someData: 123 },
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("populates top-level event fields correctly", () => {
|
|
239
|
+
const result = getActivityEvent(
|
|
240
|
+
emptySchema,
|
|
241
|
+
makeActivityUpdate({
|
|
242
|
+
type: "documentCreate",
|
|
243
|
+
name: "Test",
|
|
244
|
+
initialMandatorySecurity: { classification: [], markings: [] },
|
|
245
|
+
}),
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(result).toMatchObject({
|
|
249
|
+
eventId: "event-1",
|
|
250
|
+
aggregationKey: "agg-1",
|
|
251
|
+
createdBy: "user-1",
|
|
252
|
+
isRead: false,
|
|
253
|
+
});
|
|
254
|
+
expect(result!.createdInstant).toEqual(
|
|
255
|
+
new Date("2026-01-01T00:00:00Z").getTime(),
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
});
|
package/src/eventMappers.ts
CHANGED
|
@@ -29,7 +29,6 @@ import type {
|
|
|
29
29
|
ActivityEventDataDocumentMandatorySecurityUpdate,
|
|
30
30
|
ActivityEventDataDocumentRename,
|
|
31
31
|
DocumentSchema,
|
|
32
|
-
DocumentSecurityDiscretionary,
|
|
33
32
|
Model,
|
|
34
33
|
ModelData,
|
|
35
34
|
PresenceEvent,
|
|
@@ -64,136 +63,79 @@ export function getActivityEvent(
|
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
/**
|
|
68
|
-
* Platform event type names as sent by backpack.
|
|
69
|
-
* Note: Only DOCUMENT_CREATE is currently emitted by the backend.
|
|
70
|
-
* Remaining types are included for future use.
|
|
71
|
-
*/
|
|
72
|
-
const PlatformEventType = {
|
|
73
|
-
DOCUMENT_CREATE: "DocumentCreateEvent",
|
|
74
|
-
DOCUMENT_DESCRIPTION_UPDATE: "DocumentDescriptionUpdateEvent",
|
|
75
|
-
DOCUMENT_RENAME: "DocumentRenameEvent",
|
|
76
|
-
DOCUMENT_MANDATORY_SECURITY_UPDATE: "DocumentMandatorySecurityUpdateEvent",
|
|
77
|
-
DOCUMENT_DISCRETIONARY_SECURITY_UPDATE: "DocumentDiscretionarySecurityUpdateEvent",
|
|
78
|
-
} as const;
|
|
79
|
-
|
|
80
|
-
interface WireDocumentCreateEvent {
|
|
81
|
-
readonly name?: string;
|
|
82
|
-
readonly initialMandatorySecurity?: {
|
|
83
|
-
readonly classification?: readonly string[];
|
|
84
|
-
readonly markings?: readonly string[];
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
interface WireDocumentRenameEvent {
|
|
89
|
-
readonly previousName?: string;
|
|
90
|
-
readonly newName?: string;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
interface WireDocumentDescriptionUpdateEvent {
|
|
94
|
-
readonly newDescription?: string;
|
|
95
|
-
readonly isInitial?: boolean;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
interface WireDocumentMandatorySecurityUpdateEvent {
|
|
99
|
-
readonly newClassification?: readonly string[];
|
|
100
|
-
readonly newMarkings?: readonly string[];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
interface WireDocumentDiscretionarySecurityUpdateEvent {
|
|
104
|
-
readonly principalType: "ALL_PRINCIPAL" | "USER";
|
|
105
|
-
readonly previousDiscretionarySecurity?: DocumentSecurityDiscretionary;
|
|
106
|
-
readonly newDiscretionarySecurity: DocumentSecurityDiscretionary;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
66
|
function getActivityEventData(
|
|
110
67
|
docSchema: DocumentSchema,
|
|
111
|
-
{ eventData
|
|
68
|
+
{ eventData }: FoundryActivityEvent,
|
|
112
69
|
): ActivityEventData {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
eventData.data,
|
|
116
|
-
);
|
|
117
|
-
if (platformEventData != null) {
|
|
118
|
-
return platformEventData;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Handle custom application-defined activity events
|
|
122
|
-
// TODO: validate model is valid for activity events
|
|
123
|
-
const model = docSchema[eventType];
|
|
124
|
-
if (model == null) {
|
|
125
|
-
return {
|
|
126
|
-
rawData: eventData.data,
|
|
127
|
-
rawType: eventType,
|
|
128
|
-
type: ActivityEventDataType.UNKNOWN,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// TODO: validate data against model schema
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
eventData: eventData.data as ModelData<Model>,
|
|
136
|
-
model,
|
|
137
|
-
type: ActivityEventDataType.CUSTOM_EVENT,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function getPlatformActivityEventData(
|
|
142
|
-
eventType: string,
|
|
143
|
-
data: unknown,
|
|
144
|
-
): ActivityEventData | undefined {
|
|
145
|
-
switch (eventType) {
|
|
146
|
-
case PlatformEventType.DOCUMENT_CREATE: {
|
|
147
|
-
const wireData = data as WireDocumentCreateEvent;
|
|
70
|
+
switch (eventData.type) {
|
|
71
|
+
case "documentCreate":
|
|
148
72
|
return {
|
|
149
73
|
initialMandatorySecurity: {
|
|
150
|
-
classification:
|
|
151
|
-
markings:
|
|
74
|
+
classification: eventData.initialMandatorySecurity?.classification,
|
|
75
|
+
markings: eventData.initialMandatorySecurity?.markings,
|
|
152
76
|
},
|
|
153
|
-
name:
|
|
77
|
+
name: eventData.name ?? "",
|
|
154
78
|
type: ActivityEventDataType.DOCUMENT_CREATE,
|
|
155
79
|
} satisfies ActivityEventDataDocumentCreate;
|
|
156
|
-
}
|
|
157
80
|
|
|
158
|
-
case
|
|
159
|
-
const wireData = data as WireDocumentRenameEvent;
|
|
81
|
+
case "documentRename":
|
|
160
82
|
return {
|
|
161
|
-
newName:
|
|
162
|
-
previousName:
|
|
83
|
+
newName: eventData.newName ?? "",
|
|
84
|
+
previousName: eventData.previousName ?? "",
|
|
163
85
|
type: ActivityEventDataType.DOCUMENT_RENAME,
|
|
164
86
|
} satisfies ActivityEventDataDocumentRename;
|
|
165
|
-
}
|
|
166
87
|
|
|
167
|
-
case
|
|
168
|
-
const wireData = data as WireDocumentDescriptionUpdateEvent;
|
|
88
|
+
case "documentDescriptionUpdate":
|
|
169
89
|
return {
|
|
170
|
-
isInitial:
|
|
171
|
-
newDescription:
|
|
90
|
+
isInitial: eventData.isInitial ?? false,
|
|
91
|
+
newDescription: eventData.newDescription ?? "",
|
|
172
92
|
type: ActivityEventDataType.DOCUMENT_DESCRIPTION_UPDATE,
|
|
173
93
|
} satisfies ActivityEventDataDocumentDescriptionUpdate;
|
|
174
|
-
}
|
|
175
94
|
|
|
176
|
-
case
|
|
177
|
-
const wireData = data as WireDocumentMandatorySecurityUpdateEvent;
|
|
95
|
+
case "documentMandatorySecurityUpdate":
|
|
178
96
|
return {
|
|
179
|
-
newClassification:
|
|
180
|
-
newMarkings:
|
|
97
|
+
newClassification: eventData.newClassification ?? [],
|
|
98
|
+
newMarkings: eventData.newMarkings ?? [],
|
|
181
99
|
type: ActivityEventDataType.DOCUMENT_MANDATORY_SECURITY_UPDATE,
|
|
182
100
|
} satisfies ActivityEventDataDocumentMandatorySecurityUpdate;
|
|
183
|
-
}
|
|
184
101
|
|
|
185
|
-
case
|
|
186
|
-
const wireData = data as WireDocumentDiscretionarySecurityUpdateEvent;
|
|
102
|
+
case "documentDiscretionarySecurityUpdate":
|
|
187
103
|
return {
|
|
188
|
-
principalType:
|
|
189
|
-
previousDiscretionarySecurity:
|
|
190
|
-
newDiscretionarySecurity:
|
|
104
|
+
principalType: eventData.principalType ?? "",
|
|
105
|
+
previousDiscretionarySecurity: eventData.previousDiscretionarySecurity,
|
|
106
|
+
newDiscretionarySecurity: eventData.newDiscretionarySecurity ?? [],
|
|
191
107
|
type: ActivityEventDataType.DOCUMENT_DISCRETIONARY_SECURITY_UPDATE,
|
|
192
108
|
} satisfies ActivityEventDataDocumentDiscretionarySecurityUpdate;
|
|
109
|
+
|
|
110
|
+
case "documentCustomEvent": {
|
|
111
|
+
const { eventType, data } = eventData;
|
|
112
|
+
// TODO: validate model is valid for activity events
|
|
113
|
+
const model = docSchema[eventType];
|
|
114
|
+
if (model == null) {
|
|
115
|
+
return {
|
|
116
|
+
rawData: data,
|
|
117
|
+
rawType: eventType,
|
|
118
|
+
type: ActivityEventDataType.UNKNOWN,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// TODO: validate data against model schema
|
|
123
|
+
return {
|
|
124
|
+
data: data as ModelData<Model>,
|
|
125
|
+
eventType,
|
|
126
|
+
model,
|
|
127
|
+
type: ActivityEventDataType.CUSTOM_EVENT,
|
|
128
|
+
};
|
|
193
129
|
}
|
|
194
130
|
|
|
195
|
-
default:
|
|
196
|
-
|
|
131
|
+
default: {
|
|
132
|
+
const unknownEventData = eventData as Record<string, unknown>;
|
|
133
|
+
return {
|
|
134
|
+
rawData: eventData,
|
|
135
|
+
rawType: typeof unknownEventData.type === "string" ? unknownEventData.type : "unknown",
|
|
136
|
+
type: ActivityEventDataType.UNKNOWN,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
197
139
|
}
|
|
198
140
|
}
|
|
199
141
|
|