@palantir/pack.state.foundry 0.8.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 +26 -0
- package/build/browser/index.js +54 -51
- package/build/browser/index.js.map +1 -1
- package/build/cjs/index.cjs +54 -51
- package/build/cjs/index.cjs.map +1 -1
- package/build/esm/index.js +54 -51
- 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 +6 -6
- package/src/FoundryDocumentService.ts +6 -1
- 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 +55 -94
|
@@ -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
|
@@ -25,8 +25,9 @@ import type {
|
|
|
25
25
|
ActivityEventData,
|
|
26
26
|
ActivityEventDataDocumentCreate,
|
|
27
27
|
ActivityEventDataDocumentDescriptionUpdate,
|
|
28
|
+
ActivityEventDataDocumentDiscretionarySecurityUpdate,
|
|
29
|
+
ActivityEventDataDocumentMandatorySecurityUpdate,
|
|
28
30
|
ActivityEventDataDocumentRename,
|
|
29
|
-
ActivityEventDataDocumentSecurityUpdate,
|
|
30
31
|
DocumentSchema,
|
|
31
32
|
Model,
|
|
32
33
|
ModelData,
|
|
@@ -62,119 +63,79 @@ export function getActivityEvent(
|
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
/**
|
|
66
|
-
* Platform event type names as sent by backpack.
|
|
67
|
-
* Note: Only DOCUMENT_CREATE is currently emitted by the backend.
|
|
68
|
-
* Remaining types are included for future use.
|
|
69
|
-
*/
|
|
70
|
-
const PlatformEventType = {
|
|
71
|
-
DOCUMENT_CREATE: "DocumentCreateEvent",
|
|
72
|
-
DOCUMENT_DESCRIPTION_UPDATE: "DocumentDescriptionUpdateEvent",
|
|
73
|
-
DOCUMENT_RENAME: "DocumentRenameEvent",
|
|
74
|
-
DOCUMENT_SECURITY_UPDATE: "DocumentMandatorySecurityUpdateEvent",
|
|
75
|
-
} as const;
|
|
76
|
-
|
|
77
|
-
interface WireDocumentCreateEvent {
|
|
78
|
-
readonly name?: string;
|
|
79
|
-
readonly initialMandatorySecurity?: {
|
|
80
|
-
readonly classification?: readonly string[];
|
|
81
|
-
readonly markings?: readonly string[];
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
interface WireDocumentRenameEvent {
|
|
86
|
-
readonly previousName?: string;
|
|
87
|
-
readonly newName?: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface WireDocumentDescriptionUpdateEvent {
|
|
91
|
-
readonly newDescription?: string;
|
|
92
|
-
readonly isInitial?: boolean;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface WireDocumentSecurityUpdateEvent {
|
|
96
|
-
readonly newClassification?: readonly string[];
|
|
97
|
-
readonly newMarkings?: readonly string[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
66
|
function getActivityEventData(
|
|
101
67
|
docSchema: DocumentSchema,
|
|
102
|
-
{ eventData
|
|
68
|
+
{ eventData }: FoundryActivityEvent,
|
|
103
69
|
): ActivityEventData {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
eventData.data,
|
|
107
|
-
);
|
|
108
|
-
if (platformEventData != null) {
|
|
109
|
-
return platformEventData;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Handle custom application-defined activity events
|
|
113
|
-
// TODO: validate model is valid for activity events
|
|
114
|
-
const model = docSchema[eventType];
|
|
115
|
-
if (model == null) {
|
|
116
|
-
return {
|
|
117
|
-
rawData: eventData.data,
|
|
118
|
-
rawType: eventType,
|
|
119
|
-
type: ActivityEventDataType.UNKNOWN,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// TODO: validate data against model schema
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
eventData: eventData.data as ModelData<Model>,
|
|
127
|
-
model,
|
|
128
|
-
type: ActivityEventDataType.CUSTOM_EVENT,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function getPlatformActivityEventData(
|
|
133
|
-
eventType: string,
|
|
134
|
-
data: unknown,
|
|
135
|
-
): ActivityEventData | undefined {
|
|
136
|
-
switch (eventType) {
|
|
137
|
-
case PlatformEventType.DOCUMENT_CREATE: {
|
|
138
|
-
const wireData = data as WireDocumentCreateEvent;
|
|
70
|
+
switch (eventData.type) {
|
|
71
|
+
case "documentCreate":
|
|
139
72
|
return {
|
|
140
73
|
initialMandatorySecurity: {
|
|
141
|
-
classification:
|
|
142
|
-
markings:
|
|
74
|
+
classification: eventData.initialMandatorySecurity?.classification,
|
|
75
|
+
markings: eventData.initialMandatorySecurity?.markings,
|
|
143
76
|
},
|
|
144
|
-
name:
|
|
77
|
+
name: eventData.name ?? "",
|
|
145
78
|
type: ActivityEventDataType.DOCUMENT_CREATE,
|
|
146
79
|
} satisfies ActivityEventDataDocumentCreate;
|
|
147
|
-
}
|
|
148
80
|
|
|
149
|
-
case
|
|
150
|
-
const wireData = data as WireDocumentRenameEvent;
|
|
81
|
+
case "documentRename":
|
|
151
82
|
return {
|
|
152
|
-
newName:
|
|
153
|
-
previousName:
|
|
83
|
+
newName: eventData.newName ?? "",
|
|
84
|
+
previousName: eventData.previousName ?? "",
|
|
154
85
|
type: ActivityEventDataType.DOCUMENT_RENAME,
|
|
155
86
|
} satisfies ActivityEventDataDocumentRename;
|
|
156
|
-
}
|
|
157
87
|
|
|
158
|
-
case
|
|
159
|
-
const wireData = data as WireDocumentDescriptionUpdateEvent;
|
|
88
|
+
case "documentDescriptionUpdate":
|
|
160
89
|
return {
|
|
161
|
-
isInitial:
|
|
162
|
-
newDescription:
|
|
90
|
+
isInitial: eventData.isInitial ?? false,
|
|
91
|
+
newDescription: eventData.newDescription ?? "",
|
|
163
92
|
type: ActivityEventDataType.DOCUMENT_DESCRIPTION_UPDATE,
|
|
164
93
|
} satisfies ActivityEventDataDocumentDescriptionUpdate;
|
|
165
|
-
}
|
|
166
94
|
|
|
167
|
-
case
|
|
168
|
-
|
|
95
|
+
case "documentMandatorySecurityUpdate":
|
|
96
|
+
return {
|
|
97
|
+
newClassification: eventData.newClassification ?? [],
|
|
98
|
+
newMarkings: eventData.newMarkings ?? [],
|
|
99
|
+
type: ActivityEventDataType.DOCUMENT_MANDATORY_SECURITY_UPDATE,
|
|
100
|
+
} satisfies ActivityEventDataDocumentMandatorySecurityUpdate;
|
|
101
|
+
|
|
102
|
+
case "documentDiscretionarySecurityUpdate":
|
|
169
103
|
return {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
104
|
+
principalType: eventData.principalType ?? "",
|
|
105
|
+
previousDiscretionarySecurity: eventData.previousDiscretionarySecurity,
|
|
106
|
+
newDiscretionarySecurity: eventData.newDiscretionarySecurity ?? [],
|
|
107
|
+
type: ActivityEventDataType.DOCUMENT_DISCRETIONARY_SECURITY_UPDATE,
|
|
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
|
+
};
|
|
174
129
|
}
|
|
175
130
|
|
|
176
|
-
default:
|
|
177
|
-
|
|
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
|
+
}
|
|
178
139
|
}
|
|
179
140
|
}
|
|
180
141
|
|