@palantir/pack.document-schema.model-types 0.14.0 → 0.24.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.
@@ -0,0 +1,75 @@
1
+ /*
2
+ * Copyright 2025 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
+ /**
18
+ * Reason a document channel subscription (data, presence, activity) failed.
19
+ * UI can branch on this to decide how to react.
20
+ */
21
+ export const ChannelErrorCode = {
22
+ /** Client's max supported schema version is below the document's operational version. */
23
+ CLIENT_VERSION_TOO_LOW: "clientVersionTooLow",
24
+ /** Client's base revision is too far behind to sync. */
25
+ REVISION_TOO_OLD: "revisionTooOld",
26
+ /** Document's operational version was bumped; client must re-subscribe. */
27
+ OPERATIONAL_VERSION_BUMPED: "operationalVersionBumped",
28
+ /** Server-side internal error. */
29
+ INTERNAL_ERROR: "internalError",
30
+ /** Any other failure (transport error, or an unrecognized server code). */
31
+ UNKNOWN: "unknown",
32
+ } as const;
33
+ export type ChannelErrorCode = typeof ChannelErrorCode[keyof typeof ChannelErrorCode];
34
+
35
+ /**
36
+ * A typed error describing why a channel subscription failed.
37
+ */
38
+ export interface ChannelError {
39
+ readonly code: ChannelErrorCode;
40
+ /** Server-assigned id for correlating this error with backend logs. Empty for client-side errors. */
41
+ readonly errorInstanceId: string;
42
+ /** Optional human-readable detail (e.g. for UNKNOWN/client-side errors). */
43
+ readonly message?: string;
44
+ }
45
+
46
+ /**
47
+ * Wraps an arbitrary thrown value (transport failure, client-side error) as a
48
+ * ChannelError with code UNKNOWN.
49
+ */
50
+ export function toUnknownChannelError(cause: unknown): ChannelError {
51
+ return {
52
+ code: ChannelErrorCode.UNKNOWN,
53
+ errorInstanceId: "",
54
+ message: cause instanceof Error ? cause.message : String(cause),
55
+ };
56
+ }
57
+
58
+ // Server (platform) error code strings → domain ChannelErrorCode.
59
+ const SERVER_ERROR_CODE_MAP: Record<string, ChannelErrorCode> = {
60
+ CLIENT_VERSION_TOO_LOW: ChannelErrorCode.CLIENT_VERSION_TOO_LOW,
61
+ REVISION_TOO_OLD: ChannelErrorCode.REVISION_TOO_OLD,
62
+ DOCUMENT_TYPE_OPERATIONAL_VERSION_BUMPED: ChannelErrorCode.OPERATIONAL_VERSION_BUMPED,
63
+ INTERNAL_ERROR: ChannelErrorCode.INTERNAL_ERROR,
64
+ };
65
+
66
+ /**
67
+ * Maps a platform channel error (raw server code + instance id) to a domain
68
+ * ChannelError. Unrecognized server codes collapse to ChannelErrorCode.UNKNOWN.
69
+ */
70
+ export function toChannelError(serverCode: string, errorInstanceId: string): ChannelError {
71
+ return {
72
+ code: SERVER_ERROR_CODE_MAP[serverCode] ?? ChannelErrorCode.UNKNOWN,
73
+ errorInstanceId,
74
+ };
75
+ }
@@ -59,6 +59,7 @@ export const ExternalRefType = {
59
59
  DOC_REF: "docRef",
60
60
  MEDIA_REF: "mediaRef",
61
61
  OBJECT_REF: "objectRef",
62
+ RESOURCE_REF: "resourceRef",
62
63
  USER_REF: "userRef",
63
64
  } as const;
64
65
 
@@ -67,8 +68,9 @@ export type ExternalRefType = typeof ExternalRefType[keyof typeof ExternalRefTyp
67
68
  export interface RecordModelMetadata<T = unknown> {
68
69
  /**
69
70
  * Which fields in the model are external references (e.g. UserRef, DocumentRef, etc).
71
+ * Partial — only ref fields are listed, not every field in the model.
70
72
  */
71
- readonly externalRefFieldTypes?: Readonly<Record<keyof T, ExternalRefType>>;
73
+ readonly externalRefFieldTypes?: Readonly<Partial<Record<keyof T, ExternalRefType>>>;
72
74
  /**
73
75
  * The name of the model (should match the typescript symbol).
74
76
  */
@@ -17,6 +17,7 @@
17
17
  import type { Flavored } from "@palantir/pack.core";
18
18
  import type { DocumentRef } from "./DocumentRef.js";
19
19
  import type { Model, ModelData } from "./Model.js";
20
+ import type { RecordValidationError } from "./RecordValidation.js";
20
21
  import type { Unsubscribe } from "./Unsubscribe.js";
21
22
 
22
23
  export type RecordId = Flavored<"RecordId">;
@@ -76,6 +77,33 @@ export interface RecordRef<M extends Model = Model> {
76
77
  */
77
78
  onChange(callback: (snapshot: ModelData<M>, recordRef: RecordRef<M>) => void): Unsubscribe;
78
79
 
80
+ /**
81
+ * Subscribe to schema-validation failures for the record.
82
+ *
83
+ * If the record's stored data cannot be validated against the model's
84
+ * schema (for example, after a partial write left the document in a
85
+ * corrupted state), `onChange` subscribers are NOT called with the invalid
86
+ * data; instead, callbacks registered here receive a
87
+ * {@link RecordValidationError} describing the failure. If the record is
88
+ * later repaired by a subsequent update, `onChange` resumes firing.
89
+ *
90
+ * If the record is already invalid at subscription time, the callback is
91
+ * invoked immediately.
92
+ *
93
+ * @param callback The callback to invoke when the record fails validation.
94
+ * @returns An unsubscribe function.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * recordRef.onInvalid((error, recordRef) => {
99
+ * console.warn("Record failed validation:", error.issues);
100
+ * });
101
+ * ```
102
+ */
103
+ onInvalid(
104
+ callback: (error: RecordValidationError, recordRef: RecordRef<M>) => void,
105
+ ): Unsubscribe;
106
+
79
107
  /**
80
108
  * Subscribe to deletion of the record.
81
109
  * @param callback The callback to invoke when the record is deleted.
@@ -0,0 +1,62 @@
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 { RecordId } from "./RecordRef.js";
18
+
19
+ /**
20
+ * A single schema-validation failure within a record snapshot.
21
+ */
22
+ export interface RecordValidationIssue {
23
+ /** Path of the failing field within the record (empty for record-level issues). */
24
+ readonly path: ReadonlyArray<string | number>;
25
+ /** Human-readable description of the failure. */
26
+ readonly message: string;
27
+ }
28
+
29
+ /**
30
+ * Describes why a record's stored data failed schema validation against its
31
+ * model.
32
+ *
33
+ * Delivered to {@link RecordRef.onInvalid} subscribers and queryable per
34
+ * document via `getInvalidRecords`. A record can become invalid when the
35
+ * document's persisted state does not match what the model's schema promises
36
+ * (for example, after a partial write left the document corrupted).
37
+ */
38
+ export interface RecordValidationError {
39
+ /** Name of the model the record was expected to conform to. */
40
+ readonly modelName: string;
41
+ /** Id of the failing record. */
42
+ readonly recordId: RecordId;
43
+ /** Individual field-level failures. */
44
+ readonly issues: readonly RecordValidationIssue[];
45
+ /** Summary message suitable for logging. */
46
+ readonly message: string;
47
+ }
48
+
49
+ /**
50
+ * Error used to reject promise-based snapshot reads (e.g.
51
+ * {@link RecordRef.getSnapshot}) when the record exists but its data fails
52
+ * schema validation.
53
+ */
54
+ export class RecordInvalidError extends Error {
55
+ readonly validation: RecordValidationError;
56
+
57
+ constructor(validation: RecordValidationError) {
58
+ super(validation.message);
59
+ this.name = "RecordInvalidError";
60
+ this.validation = validation;
61
+ }
62
+ }
@@ -0,0 +1,20 @@
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 { Flavored } from "@palantir/pack.core";
18
+
19
+ /** Identifies an arbitrary platform resource by RID. */
20
+ export type ResourceRid = Flavored<"ResourceRid">;