@tellescope/schema 1.253.1 → 1.254.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/schema",
3
- "version": "1.253.1",
3
+ "version": "1.254.1",
4
4
  "description": "Schema definition for Tellescope models",
5
5
  "main": "./lib/cjs/schema.js",
6
6
  "module": "./lib/esm/schema.js",
@@ -23,13 +23,13 @@
23
23
  },
24
24
  "homepage": "https://github.com/tellescope-os/tellescope#readme",
25
25
  "dependencies": {
26
- "@tellescope/constants": "1.253.1",
27
- "@tellescope/types-client": "1.253.1",
28
- "@tellescope/types-models": "1.253.1",
29
- "@tellescope/types-server": "1.253.1",
30
- "@tellescope/types-utilities": "1.253.1",
31
- "@tellescope/utilities": "1.253.1",
32
- "@tellescope/validation": "1.253.1",
26
+ "@tellescope/constants": "1.254.1",
27
+ "@tellescope/types-client": "1.254.1",
28
+ "@tellescope/types-models": "1.254.1",
29
+ "@tellescope/types-server": "1.254.1",
30
+ "@tellescope/types-utilities": "1.254.1",
31
+ "@tellescope/utilities": "1.254.1",
32
+ "@tellescope/validation": "1.254.1",
33
33
  "validator": "13.11.0"
34
34
  },
35
35
  "devDependencies": {
@@ -43,5 +43,5 @@
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  },
46
- "gitHead": "5013042cc249a11312734f97835843f30b4249b2"
46
+ "gitHead": "28444816447e007bba693810c8ebad150b83a9a5"
47
47
  }
package/src/schema.ts CHANGED
@@ -90,7 +90,9 @@ import {
90
90
  AIDecisionAutomationAction,
91
91
  AutomationAction,
92
92
  TimeTrackTimestamp,
93
+ TimeTrackReviewHistoryItem,
93
94
  BelugaPharmacyMapping,
95
+ CompoundFilter,
94
96
  BelugaAutomationMappingEntry,
95
97
  BelugaUpdateVisitPatientPreferenceItem,
96
98
  TimeTrack,
@@ -172,6 +174,7 @@ import {
172
174
  fileSizeValidator,
173
175
  meetingStatusValidator,
174
176
  listOfAttendeesValidator,
177
+ listOfVideoCallParticipantEventsValidator,
175
178
  meetingInfoValidator,
176
179
  listOfUserIndentitiesValidator,
177
180
  meetingsListValidator,
@@ -287,6 +290,7 @@ import {
287
290
  userCallRoutingBehaviorValidator,
288
291
  userUIRestrictionsValidator,
289
292
  userFieldRedactionsValidator,
293
+ portalSchemaRestrictionsValidator,
290
294
  externalChatGPTMessagesValidator,
291
295
  enduserProfileViewBlocksValidator,
292
296
  customDashboardBlocksValidator,
@@ -2644,6 +2648,7 @@ export const schema: SchemaV1 = build_schema({
2644
2648
  info: {},
2645
2649
  fields: {
2646
2650
  ...BuiltInFields,
2651
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
2647
2652
  archivedAt: { validator: dateOptionalOrEmptyStringValidator },
2648
2653
  title: {
2649
2654
  validator: stringValidator100,
@@ -3185,9 +3190,21 @@ export const schema: SchemaV1 = build_schema({
3185
3190
  },
3186
3191
  chat_rooms: {
3187
3192
  info: {},
3188
- constraints: {
3193
+ constraints: {
3189
3194
  unique: [{ array: 'userIds' }, { array: 'enduserIds' }],
3190
- relationship: [],
3195
+ relationship: [
3196
+ {
3197
+ // Patient-initiated rooms targeting specific care-team staff (userIds) are an intended
3198
+ // portal feature and are left unrestricted. An enduser may only add THEMSELVES to a room
3199
+ // they create — they cannot add other patients (cross-patient PHI broadcast).
3200
+ explanation: 'Endusers can only add their own id to enduserIds on chat rooms they create',
3201
+ evaluate: (doc, _, session) => {
3202
+ if (session.type !== ENDUSER_SESSION_TYPE) return
3203
+ if ((doc.enduserIds ?? []).some(id => id !== session.id))
3204
+ return "enduserIds may only contain your own id for enduser session"
3205
+ }
3206
+ },
3207
+ ],
3191
3208
  access: [
3192
3209
  { type: 'filter', field: 'userIds' },
3193
3210
  { type: 'filter', field: 'enduserIds' },
@@ -3486,7 +3503,9 @@ export const schema: SchemaV1 = build_schema({
3486
3503
  {
3487
3504
  explanation: "Only admin users can update tags when accessTags is enabled",
3488
3505
  evaluate: ({ _id }, _, session, method, { updates } ) => {
3489
- if (session.type === 'user' && !session.eat) return // accessTags is not enabled
3506
+ // editing a user's tags is a privilege-escalation vector whenever tags gate visibility:
3507
+ // enduser access tags (eat) OR resource access tags (erat). Skip only when neither is enabled.
3508
+ if (session.type === 'user' && !session.eat && !(session as UserSession)?.erat) return // neither access-tags feature enabled
3490
3509
  if ((session as UserSession)?.roles?.includes('Admin')) return
3491
3510
  if (method === 'create') return
3492
3511
  if (!updates?.tags) return
@@ -4182,7 +4201,8 @@ export const schema: SchemaV1 = build_schema({
4182
4201
  },
4183
4202
  },
4184
4203
  fields: {
4185
- ...BuiltInFields,
4204
+ ...BuiltInFields,
4205
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
4186
4206
  archivedAt: { validator: dateOptionalOrEmptyStringValidator },
4187
4207
  mmsAttachmentURLs: { validator: listOfUniqueStringsValidatorEmptyOk },
4188
4208
  title: {
@@ -4230,7 +4250,8 @@ export const schema: SchemaV1 = build_schema({
4230
4250
  defaultActions: { read: {}, readMany: {}, update: {}, delete: {} },
4231
4251
  enduserActions: { prepare_file_upload: {}, confirm_file_upload: {}, file_download_URL: {}, read: {}, readMany: {}, delete: {}, update: { } /* allow to hide from client side */ },
4232
4252
  fields: {
4233
- ...BuiltInFields,
4253
+ ...BuiltInFields,
4254
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
4234
4255
  source: { validator: stringValidator100 },
4235
4256
  tags: { validator: listOfStringsValidatorUniqueOptionalOrEmptyOkay },
4236
4257
  name: {
@@ -4499,7 +4520,7 @@ export const schema: SchemaV1 = build_schema({
4499
4520
  },
4500
4521
  enduserActions: { create: {}, read: {}, readMany: {} },
4501
4522
  fields: {
4502
- ...BuiltInFields,
4523
+ ...BuiltInFields,
4503
4524
  title: {
4504
4525
  validator: stringValidator1000,
4505
4526
  required: true,
@@ -4824,6 +4845,7 @@ export const schema: SchemaV1 = build_schema({
4824
4845
  },
4825
4846
  fields: {
4826
4847
  ...BuiltInFields,
4848
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
4827
4849
  showByUserTags: { validator: listOfStringsValidatorOptionalOrEmptyOk },
4828
4850
  belugaVisitType: { validator: stringValidator },
4829
4851
  belugaVerificationId: { validator: stringValidator },
@@ -4837,8 +4859,9 @@ export const schema: SchemaV1 = build_schema({
4837
4859
  },
4838
4860
  belugaCombineMatchingPharmacyMappings: { validator: booleanValidator },
4839
4861
  mdiCaseOfferings: {
4840
- validator: listValidatorOptionalOrEmptyOk(objectValidator<{ offering_id: string }>({
4862
+ validator: listValidatorOptionalOrEmptyOk(objectValidator<{ offering_id: string, conditions?: CompoundFilter<string> }>({
4841
4863
  offering_id: stringValidator100,
4864
+ conditions: compoundFilterValidator,
4842
4865
  }))
4843
4866
  },
4844
4867
  autoMergeOnSubmission: { validator: booleanValidator },
@@ -5945,6 +5968,7 @@ export const schema: SchemaV1 = build_schema({
5945
5968
  meetingId: { validator: mongoIdStringValidator, readonly: true },
5946
5969
  bookingPageId: { validator: mongoIdStringValidator }, // allows rescheduling via booking page
5947
5970
  meetingStatus: { validator: meetingStatusValidator },
5971
+ videoCallAttendance: { validator: listOfVideoCallParticipantEventsValidator, readonly: true },
5948
5972
  attachments: { validator: listOfGenericAttachmentsValidator },
5949
5973
  cancelledAt: { validator: dateOptionalOrEmptyStringValidator },
5950
5974
  rescheduledAt: { validator: dateOptionalOrEmptyStringValidator },
@@ -6079,7 +6103,8 @@ export const schema: SchemaV1 = build_schema({
6079
6103
  customActions: {},
6080
6104
  enduserActions: { read: {}, readMany: {} },
6081
6105
  fields: {
6082
- ...BuiltInFields,
6106
+ ...BuiltInFields,
6107
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
6083
6108
  dontSyncToElation: { validator: booleanValidator },
6084
6109
  sendIcsEmail: { validator: booleanValidator },
6085
6110
  createAndBookAthenaSlot: { validator: booleanValidator },
@@ -6446,8 +6471,16 @@ export const schema: SchemaV1 = build_schema({
6446
6471
  enduser_observations: {
6447
6472
  info: {},
6448
6473
  constraints: {
6449
- unique: [],
6450
- relationship: [],
6474
+ unique: [],
6475
+ relationship: [
6476
+ {
6477
+ explanation: 'When created by an enduser, enduserId must match their id',
6478
+ evaluate: ({ enduserId },_,session) => {
6479
+ if (session.type === ENDUSER_SESSION_TYPE && session.id !== enduserId)
6480
+ return "enduserId does not match creator id for enduser session"
6481
+ }
6482
+ },
6483
+ ],
6451
6484
  },
6452
6485
  defaultActions: {
6453
6486
  ...DEFAULT_OPERATIONS,
@@ -6548,11 +6581,23 @@ export const schema: SchemaV1 = build_schema({
6548
6581
  managed_content_records: {
6549
6582
  info: {},
6550
6583
  constraints: {
6551
- unique: [],
6552
- relationship: [],
6584
+ unique: [],
6585
+ relationship: [
6586
+ {
6587
+ explanation: 'Endusers cannot self-publish managed content or bind it to another enduser',
6588
+ evaluate: (doc, _, session) => {
6589
+ if (session.type !== ENDUSER_SESSION_TYPE) return
6590
+ if (doc.publicRead) return "publicRead cannot be set by endusers"
6591
+ if (doc.allowUnauthenticatedAccess) return "allowUnauthenticatedAccess cannot be set by endusers"
6592
+ if (doc.forInternalUse) return "forInternalUse cannot be set by endusers"
6593
+ if (doc.enduserId !== undefined && doc.enduserId !== session.id)
6594
+ return "enduserId does not match creator id for enduser session"
6595
+ }
6596
+ },
6597
+ ],
6553
6598
  },
6554
6599
  defaultActions: DEFAULT_OPERATIONS,
6555
- customActions: {
6600
+ customActions: {
6556
6601
  update_indexes: {
6557
6602
  op: "custom", access: 'update', method: "patch",
6558
6603
  name: 'Update Indexes',
@@ -6607,7 +6652,8 @@ export const schema: SchemaV1 = build_schema({
6607
6652
  search: {},
6608
6653
  },
6609
6654
  fields: {
6610
- ...BuiltInFields,
6655
+ ...BuiltInFields,
6656
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
6611
6657
  slug: { validator: stringValidator250 },
6612
6658
  title: {
6613
6659
  validator: stringValidator1000,
@@ -7623,6 +7669,12 @@ export const schema: SchemaV1 = build_schema({
7623
7669
  },
7624
7670
  uiRestrictions: { validator: userUIRestrictionsValidator },
7625
7671
  fieldRedactions: { validator: userFieldRedactionsValidator },
7672
+ portalSchemaRestrictions: {
7673
+ validator: portalSchemaRestrictionsValidator,
7674
+ initializer: () => ({ disableEditContent: true, disableEditTheming: true, disableEditSnippets: true }),
7675
+ },
7676
+ color: { validator: stringValidator1000 },
7677
+ description: { validator: stringValidator1000Optional },
7626
7678
  }
7627
7679
  },
7628
7680
  appointment_booking_pages: {
@@ -7666,7 +7718,8 @@ export const schema: SchemaV1 = build_schema({
7666
7718
  read: {}, readMany: {}, validate_access_token: {},
7667
7719
  },
7668
7720
  fields: {
7669
- ...BuiltInFields,
7721
+ ...BuiltInFields,
7722
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
7670
7723
  dontRestrictRescheduleToOriginalHost: { validator: booleanValidator },
7671
7724
  gtmTag: { validator: stringValidator100EscapeHTML },
7672
7725
  archivedAt: { validator: dateOptionalOrEmptyStringValidator },
@@ -8503,7 +8556,8 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
8503
8556
  },
8504
8557
  enduserActions: {},
8505
8558
  fields: {
8506
- ...BuiltInFields,
8559
+ ...BuiltInFields,
8560
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
8507
8561
  title: {
8508
8562
  validator: stringValidator100,
8509
8563
  required: true,
@@ -8858,7 +8912,8 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
8858
8912
  customActions: {},
8859
8913
  enduserActions: {},
8860
8914
  fields: {
8861
- ...BuiltInFields,
8915
+ ...BuiltInFields,
8916
+ accessTags: { redactions: ['enduser'], validator: listOfStringsValidatorEmptyOk },
8862
8917
  title: {
8863
8918
  validator: stringValidator,
8864
8919
  required: true,
@@ -9040,14 +9095,33 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
9040
9095
  }
9041
9096
  },
9042
9097
  {
9043
- explanation: "Locked time tracks only allow review field updates",
9044
- evaluate: (_v, _deps, _session, method, { original, updates }) => {
9098
+ explanation: "Locked time tracks only allow review field updates, or owner resubmission of rejected entries",
9099
+ evaluate: (_v, _deps, session, method, { original, updates }) => {
9045
9100
  if (method !== 'update') return
9046
9101
  const orig = original as any as TimeTrack | undefined
9047
9102
  if (!orig?.lockedAt) return
9103
+
9104
+ const u = updates as any as Partial<TimeTrack> | undefined
9048
9105
  const reviewFields = ['reviewedAt', 'reviewedByUserId', 'reviewApproved', 'reviewNote']
9049
9106
  const nonReviewFields = Object.keys(updates || {}).filter(k => !reviewFields.includes(k))
9050
- if (nonReviewFields.length > 0) return `Time track is locked. Only review fields (${reviewFields.join(', ')}) can be updated.`
9107
+ if (nonReviewFields.length === 0) return // pure review update unchanged behavior
9108
+
9109
+ const isRejected = !!orig.reviewedAt && orig.reviewApproved === false
9110
+ const resubmitFields = [
9111
+ ...reviewFields,
9112
+ 'correctedAt', 'correctedByUserId', 'correctionNote',
9113
+ 'originalTotalDurationInMS', 'totalDurationInMS',
9114
+ 'lockedAt', 'lockedByUserId',
9115
+ ]
9116
+ const disallowed = Object.keys(updates || {}).filter(k => !resubmitFields.includes(k))
9117
+
9118
+ if (isRejected && session.id === orig.userId && disallowed.length === 0) {
9119
+ if (u?.reviewedAt !== '') return "Resubmitting a rejected time track requires clearing reviewedAt (set to '')"
9120
+ if (u?.reviewApproved === true) return "Cannot approve your own time track during resubmission"
9121
+ return // valid resubmit — entry returns to Pending
9122
+ }
9123
+
9124
+ return `Time track is locked. Only review fields (${reviewFields.join(', ')}) can be updated.`
9051
9125
  }
9052
9126
  },
9053
9127
  {
@@ -9132,6 +9206,19 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
9132
9206
  reviewNote: { validator: stringValidator1000Optional },
9133
9207
  lockedAt: { validator: dateValidatorOptional },
9134
9208
  lockedByUserId: { validator: mongoIdStringOptional },
9209
+ // server-appended on resubmission of a rejected entry (see routing.ts update handler)
9210
+ // note: reviewApproved remains false after resubmit (booleans can't be cleared to '') — status logic must key off reviewedAt
9211
+ reviewHistory: {
9212
+ validator: listValidatorOptionalOrEmptyOk(objectValidator<TimeTrackReviewHistoryItem>({
9213
+ reviewedAt: dateValidator,
9214
+ reviewedByUserId: mongoIdStringOptional,
9215
+ reviewApproved: booleanValidatorOptional,
9216
+ reviewNote: stringValidator1000Optional,
9217
+ resubmittedAt: dateValidatorOptional,
9218
+ resubmittedByUserId: mongoIdStringOptional,
9219
+ })),
9220
+ readonly: true, // server-appended only — clients can read, never write
9221
+ },
9135
9222
  },
9136
9223
  },
9137
9224
  ticket_queues: {
@@ -9315,8 +9402,17 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
9315
9402
  info: {
9316
9403
  description: 'Lab, medication, and device orders'
9317
9404
  },
9318
- constraints: {
9319
- unique: [], relationship: [],
9405
+ constraints: {
9406
+ unique: [],
9407
+ relationship: [
9408
+ {
9409
+ explanation: 'When created by an enduser, enduserId must match their id',
9410
+ evaluate: ({ enduserId },_,session) => {
9411
+ if (session.type === ENDUSER_SESSION_TYPE && session.id !== enduserId)
9412
+ return "enduserId does not match creator id for enduser session"
9413
+ }
9414
+ },
9415
+ ],
9320
9416
  access: [{ type: 'filter', field: 'userId' }]
9321
9417
  },
9322
9418
  defaultActions: DEFAULT_OPERATIONS,
@@ -9744,7 +9840,7 @@ If a voicemail is left, it is indicated by recordingURI, transcription, or recor
9744
9840
  fields: {
9745
9841
  ...BuiltInFields,
9746
9842
  integration: { validator: stringValidator, readonly: true, examples: ['Canvas'] },
9747
- status: { validator: exactMatchValidator(['Success', 'Error']), readonly: true, examples: ['Error'] },
9843
+ status: { validator: exactMatchValidator(['Success', 'Error', 'Info']), readonly: true, examples: ['Error'] },
9748
9844
  type: { validator: stringValidator, readonly: true, examples: ['Patient Create'] },
9749
9845
  payload: { validator: stringValidator, readonly: true, examples: ['{}'] },
9750
9846
  response: { validator: stringValidator, readonly: true, examples: ['{}'] },