@updraft-solutions/mcrit-sdk 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.
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { aB as activitySchema, aC as addressSchema, aD as aircraftPositionSchema, aE as aircraftRatingSchema, aJ as airplaneSchema, aI as airplanePermissionSchema, aK as airplaneShareInviteSchema, aL as airportSchema, aM as alertSchema, aN as allowedDocumentTypeSchema, aO as approachTypeSchema, aP as availSchema, aQ as baseMembershipSchema, aR as bookingSchema, aT as commentSchema, aU as companySchema, a_ as courseSchema, aY as courseEnrollmentSchema, aZ as courseFeeSchema, aG as airplaneIssueSeverityEnum, b4 as documentSchema, b0 as documentComplianceStatus, b1 as documentRequirement, b2 as documentRequirementGroup, b3 as documentRequirementGroupAssignment, b5 as documentShareSchema, b6 as documentType, b8 as eventSchema, ba as expenseSchema, b9 as expenseItemSchema, bb as extendedAirplaneSchema, bd as feeSchema, bc as feeCategoryOptionsSchema, be as flightLogSchema, bl as invoiceSchema, bn as landingSchema, bm as landingFeeSchema, bp as maintenanceEventSchema, bo as maintenanceEventEstimationSchema, bq as maintenanceStatusSchema, br as mediaCollection, bt as membershipSchema, bu as membershipSignupSchema, bv as postSchema, bw as prePaidPackageSchema, bx as priceSchema, by as qualificationSchema, bz as recurringFlightSchema, bA as roleSchema, bB as runwaySchema, bC as scheduledFlightSchema, bD as settingSchema, bE as specialFlightSchema, bL as tagSchema, bM as ticketSchema, bW as userSchema, bU as userAircraftRatingSchema, bV as userQualificationSchema, bX as waypointSchema, aF as airplaneIssueSchema, aH as airplaneIssueStatusEnum, B as BaseModel$1, aW as courseCheckSyllabusSchema, aX as courseEnrollmentMetricsSchema, bs as mediaSchema, l as FormSubmission } from '../training-BibD4HsC.cjs';
2
+ import { aB as activitySchema, aC as addressSchema, aD as aircraftPositionSchema, aE as aircraftRatingSchema, aJ as airplaneSchema, aI as airplanePermissionSchema, aK as airplaneShareInviteSchema, aL as airportSchema, aM as alertSchema, aN as allowedDocumentTypeSchema, aO as approachTypeSchema, aP as availSchema, aQ as baseMembershipSchema, aS as bookingSchema, aU as commentSchema, aV as companySchema, a$ as courseSchema, aZ as courseEnrollmentSchema, a_ as courseFeeSchema, aG as airplaneIssueSeverityEnum, b5 as documentSchema, b1 as documentComplianceStatus, b2 as documentRequirement, b3 as documentRequirementGroup, b4 as documentRequirementGroupAssignment, b6 as documentShareSchema, b7 as documentType, b9 as eventSchema, bb as expenseSchema, ba as expenseItemSchema, bc as extendedAirplaneSchema, be as feeSchema, bd as feeCategoryOptionsSchema, bf as flightLogSchema, bm as invoiceSchema, bo as landingSchema, bn as landingFeeSchema, bq as maintenanceEventSchema, bp as maintenanceEventEstimationSchema, br as maintenanceStatusSchema, bs as mediaCollection, bu as membershipSchema, bv as membershipSignupSchema, bw as postSchema, by as prePaidPackageSchema, bz as priceSchema, bA as qualificationSchema, bB as recurringFlightSchema, bC as roleSchema, bD as runwaySchema, bE as scheduledFlightSchema, bF as settingSchema, bG as specialFlightSchema, bN as tagSchema, bO as ticketSchema, bY as userSchema, bW as userAircraftRatingSchema, bX as userQualificationSchema, bZ as waypointSchema, aF as airplaneIssueSchema, aH as airplaneIssueStatusEnum, B as BaseModel$1, aX as courseCheckSyllabusSchema, aY as courseEnrollmentMetricsSchema, bt as mediaSchema, l as FormSubmission } from '../training-DRuphnSP.cjs';
3
3
 
4
4
  type BaseModel = z.infer<typeof BaseModel$1>;
5
5
  type User = z.infer<typeof userSchema>;
@@ -61,17 +61,33 @@ type AirplaneIssueStatus = z.infer<typeof airplaneIssueStatusEnum>;
61
61
  type Event = z.infer<typeof eventSchema>;
62
62
  type Ticket = z.infer<typeof ticketSchema>;
63
63
  type Post = z.infer<typeof postSchema>;
64
+ /**
65
+ * Full-replacement tag sync entry — `id` marks an existing tag (wins over
66
+ * name matching), fresh tags carry only name (+ optional color) and are
67
+ * created server-side on save.
68
+ */
69
+ interface TagSyncEntry {
70
+ id?: number | null;
71
+ name: string;
72
+ color?: string | null;
73
+ }
64
74
  interface CreatePostInput {
65
75
  title?: string | null;
66
76
  content: string;
67
77
  sticky?: boolean;
68
78
  public?: boolean;
79
+ comments_disabled?: boolean;
80
+ tags?: TagSyncEntry[];
81
+ /** Action flag, not persisted: notify company members on publish. */
82
+ notify?: boolean;
69
83
  }
70
84
  interface UpdatePostInput {
71
85
  title?: string | null;
72
86
  content: string;
73
87
  sticky?: boolean;
74
88
  public?: boolean;
89
+ comments_disabled?: boolean;
90
+ tags?: TagSyncEntry[];
75
91
  }
76
92
  interface CreateAirplaneIssueInput {
77
93
  title?: string | null;
@@ -277,6 +293,7 @@ interface LogbookFlightLogPayload {
277
293
  role: string;
278
294
  }[];
279
295
  membership_id: number | null;
296
+ billing_profile_id?: number | null;
280
297
  }
281
298
  interface LogbookSuggestionMeta {
282
299
  flags?: string[];
@@ -419,4 +436,4 @@ interface LandingFeeInvoiceApplyResult {
419
436
  issues: string[];
420
437
  }
421
438
 
422
- export type { Activity, Address, AiSuggestedMilestone, AiSuggestion, AiSuggestionStatus, AircraftBlock, AircraftBlockReason, AircraftPosition, AircraftRating, Airplane, AirplaneIssue, AirplaneIssueSeverity, AirplaneIssueStatus, AirplanePermission, AirplaneShareInvite, Airport, Alert, AllowedDocumentType, ApiResponse, ApproachType, Avail, BackgroundTask, BackgroundTaskStatus, BaseMembership, BaseModel, Booking, Comment, Company, ComplianceData, ComplianceGroup, ComplianceGroupSource, ComplianceRequirement, ComplianceRequirementGroup, ComplianceResponse, ComplianceSummary, Course, CourseCheckSyllabus, CourseDescriptionSuggestionPayload, CourseEnrollment, CourseEnrollmentMetrics, CourseFee, CreateAirplaneIssueInput, CreateCommentInput, CreatePostInput, DataWrappedResponse, DeferAirplaneIssueInput, DiscardedRow, Document, DocumentComplianceStatus, DocumentRequirement, DocumentRequirementGroup, DocumentRequirementGroupAssignment, DocumentShare, DocumentType, Event, Expense, ExpenseItem, ExtendedAirplane, Fee, FeeCategoryOptions, FlightLog, GroupedPermissions, Invoice, Landing, LandingFee, LandingFeeImportResult, LandingFeeInvoiceApplyPayload, LandingFeeInvoiceApplyResult, LandingFeeInvoicePreview, LandingFeeInvoicePreviewRow, LandingFeeTemplateParams, LogbookFlightLogPayload, LogbookStageResult, LogbookSuggestion, LogbookSuggestionMeta, MaintenanceEvent, MaintenanceEventEstimation, MaintenanceStatus, Media, MediaCollection, Membership, MembershipSignup, PaginatedResponse, PermissionItem, Post, PrePaidPackage, Price, Qualification, RecurringFlight, ResolveAirplaneIssueInput, Resource, Role, RoleDetail, RoleListItem, Runway, ScheduledFlight, Setting, SpecialFlight, Tag, Ticket, ToggleFavoriteResponse, UpdateAirplaneIssueInput, UpdateCommentInput, UpdatePostInput, User, UserAircraftRating, UserQualification, Waypoint };
439
+ export type { Activity, Address, AiSuggestedMilestone, AiSuggestion, AiSuggestionStatus, AircraftBlock, AircraftBlockReason, AircraftPosition, AircraftRating, Airplane, AirplaneIssue, AirplaneIssueSeverity, AirplaneIssueStatus, AirplanePermission, AirplaneShareInvite, Airport, Alert, AllowedDocumentType, ApiResponse, ApproachType, Avail, BackgroundTask, BackgroundTaskStatus, BaseMembership, BaseModel, Booking, Comment, Company, ComplianceData, ComplianceGroup, ComplianceGroupSource, ComplianceRequirement, ComplianceRequirementGroup, ComplianceResponse, ComplianceSummary, Course, CourseCheckSyllabus, CourseDescriptionSuggestionPayload, CourseEnrollment, CourseEnrollmentMetrics, CourseFee, CreateAirplaneIssueInput, CreateCommentInput, CreatePostInput, DataWrappedResponse, DeferAirplaneIssueInput, DiscardedRow, Document, DocumentComplianceStatus, DocumentRequirement, DocumentRequirementGroup, DocumentRequirementGroupAssignment, DocumentShare, DocumentType, Event, Expense, ExpenseItem, ExtendedAirplane, Fee, FeeCategoryOptions, FlightLog, GroupedPermissions, Invoice, Landing, LandingFee, LandingFeeImportResult, LandingFeeInvoiceApplyPayload, LandingFeeInvoiceApplyResult, LandingFeeInvoicePreview, LandingFeeInvoicePreviewRow, LandingFeeTemplateParams, LogbookFlightLogPayload, LogbookStageResult, LogbookSuggestion, LogbookSuggestionMeta, MaintenanceEvent, MaintenanceEventEstimation, MaintenanceStatus, Media, MediaCollection, Membership, MembershipSignup, PaginatedResponse, PermissionItem, Post, PrePaidPackage, Price, Qualification, RecurringFlight, ResolveAirplaneIssueInput, Resource, Role, RoleDetail, RoleListItem, Runway, ScheduledFlight, Setting, SpecialFlight, Tag, TagSyncEntry, Ticket, ToggleFavoriteResponse, UpdateAirplaneIssueInput, UpdateCommentInput, UpdatePostInput, User, UserAircraftRating, UserQualification, Waypoint };
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { aB as activitySchema, aC as addressSchema, aD as aircraftPositionSchema, aE as aircraftRatingSchema, aJ as airplaneSchema, aI as airplanePermissionSchema, aK as airplaneShareInviteSchema, aL as airportSchema, aM as alertSchema, aN as allowedDocumentTypeSchema, aO as approachTypeSchema, aP as availSchema, aQ as baseMembershipSchema, aR as bookingSchema, aT as commentSchema, aU as companySchema, a_ as courseSchema, aY as courseEnrollmentSchema, aZ as courseFeeSchema, aG as airplaneIssueSeverityEnum, b4 as documentSchema, b0 as documentComplianceStatus, b1 as documentRequirement, b2 as documentRequirementGroup, b3 as documentRequirementGroupAssignment, b5 as documentShareSchema, b6 as documentType, b8 as eventSchema, ba as expenseSchema, b9 as expenseItemSchema, bb as extendedAirplaneSchema, bd as feeSchema, bc as feeCategoryOptionsSchema, be as flightLogSchema, bl as invoiceSchema, bn as landingSchema, bm as landingFeeSchema, bp as maintenanceEventSchema, bo as maintenanceEventEstimationSchema, bq as maintenanceStatusSchema, br as mediaCollection, bt as membershipSchema, bu as membershipSignupSchema, bv as postSchema, bw as prePaidPackageSchema, bx as priceSchema, by as qualificationSchema, bz as recurringFlightSchema, bA as roleSchema, bB as runwaySchema, bC as scheduledFlightSchema, bD as settingSchema, bE as specialFlightSchema, bL as tagSchema, bM as ticketSchema, bW as userSchema, bU as userAircraftRatingSchema, bV as userQualificationSchema, bX as waypointSchema, aF as airplaneIssueSchema, aH as airplaneIssueStatusEnum, B as BaseModel$1, aW as courseCheckSyllabusSchema, aX as courseEnrollmentMetricsSchema, bs as mediaSchema, l as FormSubmission } from '../training-BibD4HsC.js';
2
+ import { aB as activitySchema, aC as addressSchema, aD as aircraftPositionSchema, aE as aircraftRatingSchema, aJ as airplaneSchema, aI as airplanePermissionSchema, aK as airplaneShareInviteSchema, aL as airportSchema, aM as alertSchema, aN as allowedDocumentTypeSchema, aO as approachTypeSchema, aP as availSchema, aQ as baseMembershipSchema, aS as bookingSchema, aU as commentSchema, aV as companySchema, a$ as courseSchema, aZ as courseEnrollmentSchema, a_ as courseFeeSchema, aG as airplaneIssueSeverityEnum, b5 as documentSchema, b1 as documentComplianceStatus, b2 as documentRequirement, b3 as documentRequirementGroup, b4 as documentRequirementGroupAssignment, b6 as documentShareSchema, b7 as documentType, b9 as eventSchema, bb as expenseSchema, ba as expenseItemSchema, bc as extendedAirplaneSchema, be as feeSchema, bd as feeCategoryOptionsSchema, bf as flightLogSchema, bm as invoiceSchema, bo as landingSchema, bn as landingFeeSchema, bq as maintenanceEventSchema, bp as maintenanceEventEstimationSchema, br as maintenanceStatusSchema, bs as mediaCollection, bu as membershipSchema, bv as membershipSignupSchema, bw as postSchema, by as prePaidPackageSchema, bz as priceSchema, bA as qualificationSchema, bB as recurringFlightSchema, bC as roleSchema, bD as runwaySchema, bE as scheduledFlightSchema, bF as settingSchema, bG as specialFlightSchema, bN as tagSchema, bO as ticketSchema, bY as userSchema, bW as userAircraftRatingSchema, bX as userQualificationSchema, bZ as waypointSchema, aF as airplaneIssueSchema, aH as airplaneIssueStatusEnum, B as BaseModel$1, aX as courseCheckSyllabusSchema, aY as courseEnrollmentMetricsSchema, bt as mediaSchema, l as FormSubmission } from '../training-DRuphnSP.js';
3
3
 
4
4
  type BaseModel = z.infer<typeof BaseModel$1>;
5
5
  type User = z.infer<typeof userSchema>;
@@ -61,17 +61,33 @@ type AirplaneIssueStatus = z.infer<typeof airplaneIssueStatusEnum>;
61
61
  type Event = z.infer<typeof eventSchema>;
62
62
  type Ticket = z.infer<typeof ticketSchema>;
63
63
  type Post = z.infer<typeof postSchema>;
64
+ /**
65
+ * Full-replacement tag sync entry — `id` marks an existing tag (wins over
66
+ * name matching), fresh tags carry only name (+ optional color) and are
67
+ * created server-side on save.
68
+ */
69
+ interface TagSyncEntry {
70
+ id?: number | null;
71
+ name: string;
72
+ color?: string | null;
73
+ }
64
74
  interface CreatePostInput {
65
75
  title?: string | null;
66
76
  content: string;
67
77
  sticky?: boolean;
68
78
  public?: boolean;
79
+ comments_disabled?: boolean;
80
+ tags?: TagSyncEntry[];
81
+ /** Action flag, not persisted: notify company members on publish. */
82
+ notify?: boolean;
69
83
  }
70
84
  interface UpdatePostInput {
71
85
  title?: string | null;
72
86
  content: string;
73
87
  sticky?: boolean;
74
88
  public?: boolean;
89
+ comments_disabled?: boolean;
90
+ tags?: TagSyncEntry[];
75
91
  }
76
92
  interface CreateAirplaneIssueInput {
77
93
  title?: string | null;
@@ -277,6 +293,7 @@ interface LogbookFlightLogPayload {
277
293
  role: string;
278
294
  }[];
279
295
  membership_id: number | null;
296
+ billing_profile_id?: number | null;
280
297
  }
281
298
  interface LogbookSuggestionMeta {
282
299
  flags?: string[];
@@ -419,4 +436,4 @@ interface LandingFeeInvoiceApplyResult {
419
436
  issues: string[];
420
437
  }
421
438
 
422
- export type { Activity, Address, AiSuggestedMilestone, AiSuggestion, AiSuggestionStatus, AircraftBlock, AircraftBlockReason, AircraftPosition, AircraftRating, Airplane, AirplaneIssue, AirplaneIssueSeverity, AirplaneIssueStatus, AirplanePermission, AirplaneShareInvite, Airport, Alert, AllowedDocumentType, ApiResponse, ApproachType, Avail, BackgroundTask, BackgroundTaskStatus, BaseMembership, BaseModel, Booking, Comment, Company, ComplianceData, ComplianceGroup, ComplianceGroupSource, ComplianceRequirement, ComplianceRequirementGroup, ComplianceResponse, ComplianceSummary, Course, CourseCheckSyllabus, CourseDescriptionSuggestionPayload, CourseEnrollment, CourseEnrollmentMetrics, CourseFee, CreateAirplaneIssueInput, CreateCommentInput, CreatePostInput, DataWrappedResponse, DeferAirplaneIssueInput, DiscardedRow, Document, DocumentComplianceStatus, DocumentRequirement, DocumentRequirementGroup, DocumentRequirementGroupAssignment, DocumentShare, DocumentType, Event, Expense, ExpenseItem, ExtendedAirplane, Fee, FeeCategoryOptions, FlightLog, GroupedPermissions, Invoice, Landing, LandingFee, LandingFeeImportResult, LandingFeeInvoiceApplyPayload, LandingFeeInvoiceApplyResult, LandingFeeInvoicePreview, LandingFeeInvoicePreviewRow, LandingFeeTemplateParams, LogbookFlightLogPayload, LogbookStageResult, LogbookSuggestion, LogbookSuggestionMeta, MaintenanceEvent, MaintenanceEventEstimation, MaintenanceStatus, Media, MediaCollection, Membership, MembershipSignup, PaginatedResponse, PermissionItem, Post, PrePaidPackage, Price, Qualification, RecurringFlight, ResolveAirplaneIssueInput, Resource, Role, RoleDetail, RoleListItem, Runway, ScheduledFlight, Setting, SpecialFlight, Tag, Ticket, ToggleFavoriteResponse, UpdateAirplaneIssueInput, UpdateCommentInput, UpdatePostInput, User, UserAircraftRating, UserQualification, Waypoint };
439
+ export type { Activity, Address, AiSuggestedMilestone, AiSuggestion, AiSuggestionStatus, AircraftBlock, AircraftBlockReason, AircraftPosition, AircraftRating, Airplane, AirplaneIssue, AirplaneIssueSeverity, AirplaneIssueStatus, AirplanePermission, AirplaneShareInvite, Airport, Alert, AllowedDocumentType, ApiResponse, ApproachType, Avail, BackgroundTask, BackgroundTaskStatus, BaseMembership, BaseModel, Booking, Comment, Company, ComplianceData, ComplianceGroup, ComplianceGroupSource, ComplianceRequirement, ComplianceRequirementGroup, ComplianceResponse, ComplianceSummary, Course, CourseCheckSyllabus, CourseDescriptionSuggestionPayload, CourseEnrollment, CourseEnrollmentMetrics, CourseFee, CreateAirplaneIssueInput, CreateCommentInput, CreatePostInput, DataWrappedResponse, DeferAirplaneIssueInput, DiscardedRow, Document, DocumentComplianceStatus, DocumentRequirement, DocumentRequirementGroup, DocumentRequirementGroupAssignment, DocumentShare, DocumentType, Event, Expense, ExpenseItem, ExtendedAirplane, Fee, FeeCategoryOptions, FlightLog, GroupedPermissions, Invoice, Landing, LandingFee, LandingFeeImportResult, LandingFeeInvoiceApplyPayload, LandingFeeInvoiceApplyResult, LandingFeeInvoicePreview, LandingFeeInvoicePreviewRow, LandingFeeTemplateParams, LogbookFlightLogPayload, LogbookStageResult, LogbookSuggestion, LogbookSuggestionMeta, MaintenanceEvent, MaintenanceEventEstimation, MaintenanceStatus, Media, MediaCollection, Membership, MembershipSignup, PaginatedResponse, PermissionItem, Post, PrePaidPackage, Price, Qualification, RecurringFlight, ResolveAirplaneIssueInput, Resource, Role, RoleDetail, RoleListItem, Runway, ScheduledFlight, Setting, SpecialFlight, Tag, TagSyncEntry, Ticket, ToggleFavoriteResponse, UpdateAirplaneIssueInput, UpdateCommentInput, UpdatePostInput, User, UserAircraftRating, UserQualification, Waypoint };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@updraft-solutions/mcrit-sdk",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Shared TypeScript types, Zod schemas, and pure helpers for the MCRIT aviation fleet management platform",
5
5
  "homepage": "https://github.com/updraft-solutions/mcrit-sdk#readme",
6
6
  "bugs": {
@@ -126,6 +126,17 @@
126
126
  "types": "./dist/pagination/index.d.cts",
127
127
  "default": "./dist/pagination/index.cjs"
128
128
  }
129
+ },
130
+ "./sanitize": {
131
+ "source": "./src/sanitize/index.ts",
132
+ "import": {
133
+ "types": "./dist/sanitize/index.d.ts",
134
+ "default": "./dist/sanitize/index.js"
135
+ },
136
+ "require": {
137
+ "types": "./dist/sanitize/index.d.cts",
138
+ "default": "./dist/sanitize/index.cjs"
139
+ }
129
140
  }
130
141
  },
131
142
  "files": [
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Shared HTML-sanitizing policy for TipTap rich text (posts, knowledge
3
+ * articles, …). The allow-lists live HERE so the app that writes content
4
+ * (mcrit-app) and the surfaces that render it (homepage, future clients)
5
+ * can never drift apart — a drift would mean content is sanitized
6
+ * differently where it's written versus where it's publicly shown.
7
+ *
8
+ * The SDK deliberately has no DOMPurify dependency: consumers pass their
9
+ * own instance to {@link createHtmlSanitizers} (browser DOMPurify,
10
+ * isomorphic-dompurify on SSR, …).
11
+ */
12
+
13
+ /** Structural subset of DOMPurify we rely on — keeps it a peer concern. */
14
+ export interface HtmlPurifier {
15
+ sanitize(html: string, config?: Record<string, unknown>): string;
16
+ }
17
+
18
+ /**
19
+ * Rich text from the TipTap post editor: common formatting, headings,
20
+ * links, lists, quotes and code.
21
+ */
22
+ export const richTextSanitizeConfig = {
23
+ ALLOWED_TAGS: [
24
+ "p",
25
+ "br",
26
+ "strong",
27
+ "em",
28
+ "u",
29
+ "s",
30
+ "a",
31
+ "h1",
32
+ "h2",
33
+ "h3",
34
+ "h4",
35
+ "h5",
36
+ "h6",
37
+ "ul",
38
+ "ol",
39
+ "li",
40
+ "blockquote",
41
+ "code",
42
+ "pre",
43
+ ],
44
+ ALLOWED_ATTR: ["href", "target", "rel", "class", "style"],
45
+ ALLOW_DATA_ATTR: false,
46
+ } as const;
47
+
48
+ /** Minimal inline formatting — simple comments and basic text fields. */
49
+ export const basicTextSanitizeConfig = {
50
+ ALLOWED_TAGS: ["br", "a", "strong", "em", "code", "p"],
51
+ ALLOWED_ATTR: ["href", "target", "rel", "class"],
52
+ ALLOW_DATA_ATTR: false,
53
+ } as const;
54
+
55
+ /** Everything stripped — plain-text snippets and previews. */
56
+ export const stripSanitizeConfig = {
57
+ ALLOWED_TAGS: [],
58
+ ALLOWED_ATTR: [],
59
+ } as const;
60
+
61
+ /**
62
+ * Bind the shared policies to a DOMPurify(-compatible) instance.
63
+ *
64
+ * @example
65
+ * import DOMPurify from "dompurify";
66
+ * const { sanitizeRichText, stripHtml } = createHtmlSanitizers(DOMPurify);
67
+ */
68
+ export function createHtmlSanitizers(purifier: HtmlPurifier) {
69
+ const sanitize = (
70
+ html: string | null | undefined,
71
+ config?: Record<string, unknown>,
72
+ ): string => {
73
+ if (!html) {
74
+ return "";
75
+ }
76
+ return purifier.sanitize(html, config);
77
+ };
78
+
79
+ const sanitizeRichText = (html: string | null | undefined): string =>
80
+ sanitize(html, richTextSanitizeConfig as unknown as Record<string, unknown>);
81
+
82
+ const sanitizeBasicText = (html: string | null | undefined): string =>
83
+ sanitize(html, basicTextSanitizeConfig as unknown as Record<string, unknown>);
84
+
85
+ /** Strip all HTML to whitespace-normalized plain text. */
86
+ const stripHtml = (html: string | null | undefined): string =>
87
+ sanitize(html, stripSanitizeConfig as unknown as Record<string, unknown>)
88
+ .replace(/\s+/g, " ")
89
+ .trim();
90
+
91
+ return { sanitize, sanitizeRichText, sanitizeBasicText, stripHtml };
92
+ }
@@ -735,6 +735,47 @@ export const allowedDocumentTypeSchema = z.object({
735
735
  .optional(),
736
736
  });
737
737
 
738
+ // Billing recipient (formerly "Address"; the `addresses` table was renamed
739
+ // to `billing_profiles` — see BillingProfileResource.php). A membership/user
740
+ // can own several profiles (e.g. private vs. "Firma"); invoices, flight logs
741
+ // and expense items each point at one via `billing_profile_id`.
742
+ export const billingProfileSchema = z.object({
743
+ // BillingProfileResource never actually emits a `type` key today; kept
744
+ // optional for forward-compat / discriminated-union consistency with
745
+ // sibling schemas in this file.
746
+ type: z.literal("billing_profile").optional(),
747
+ id: z.number(),
748
+ owner_type: z.string(),
749
+ owner_id: z.number(),
750
+ label: z.string().nullable(),
751
+ is_default: z.boolean(),
752
+ name: z.string().nullable(),
753
+ company: z.string().nullable(),
754
+ address_street_1: z.string().nullable(),
755
+ address_street_2: z.string().nullable(),
756
+ city: z.string().nullable(),
757
+ state: z.string().nullable(),
758
+ zip: z.string().nullable(),
759
+ phone: z.string().nullable(),
760
+ fax: z.string().nullable(),
761
+ iso_country: z.string().nullable(),
762
+ vat_id: z.string().nullable(),
763
+ email: z.string().nullable(),
764
+ archived_at: z.string().nullable(),
765
+ source_billing_profile_id: z.number().nullable(),
766
+ is_synced: z.boolean(),
767
+ frozen_at: z.string().nullable(),
768
+ // Present only when the profile is frozen (frozen_by_user_id !== null).
769
+ frozen_by: z
770
+ .object({
771
+ id: z.number(),
772
+ name: z.string().nullable().optional(),
773
+ })
774
+ .optional(),
775
+ created_at: z.string(),
776
+ updated_at: z.string(),
777
+ });
778
+
738
779
  export const flightLogSchema = z.object({
739
780
  type: z.literal("flightLog"),
740
781
  id: z.number(),
@@ -749,6 +790,7 @@ export const flightLogSchema = z.object({
749
790
  booking_id: z.number().nullable().optional(),
750
791
  membership_id: z.number().nullable().optional(),
751
792
  membership: baseMembershipSchema.optional(),
793
+ billing_profile_id: z.number().nullable().optional(),
752
794
  author_id: z.number(),
753
795
  author: userSchema.optional(),
754
796
  invoice_id: z.number().nullable().optional(),
@@ -798,6 +840,14 @@ export const invoiceSchema = z.object({
798
840
  title: z.string(),
799
841
  membership_id: z.number(),
800
842
  membership: baseMembershipSchema.optional(),
843
+ // InvoiceResource.php always emits `billing_profile_id` (non-optional key,
844
+ // nullable FK value). `billingProfile` is a `whenLoaded('billingProfile')`
845
+ // relation, so it stays optional. (Fix round 1: as of the original Task 16
846
+ // commit, InvoiceResource hadn't gained `billing_profile_id` yet — it was
847
+ // added ~4 minutes later in the same session; tightened here now that the
848
+ // key is always present.)
849
+ billing_profile_id: z.number().nullable(),
850
+ billingProfile: billingProfileSchema.optional(),
801
851
  company_id: z.number().optional(),
802
852
  company: companySchema.optional(),
803
853
  sequence_number: z.string().nullable().optional(),
@@ -825,6 +875,9 @@ export const invoiceSchema = z.object({
825
875
  recipient_state: z.string().nullable().optional(),
826
876
  recipient_country: z.string().nullable().optional(),
827
877
  recipient_vat_id: z.string().nullable().optional(),
878
+ // Always computed by InvoiceResource (billingProfile email, else the
879
+ // membership's user email) — used by InvoiceDialog today but was missing.
880
+ recipient_email: z.string().nullable().optional(),
828
881
  issuer_name: z.string().nullable().optional(),
829
882
  issuer_address_street_1: z.string().nullable().optional(),
830
883
  issuer_address_street_2: z.string().nullable().optional(),
@@ -1168,6 +1221,8 @@ export const postSchema = z.object({
1168
1221
  content: z.string(),
1169
1222
  sticky: z.boolean().default(false),
1170
1223
  public: z.boolean().default(false),
1224
+ comments_disabled: z.boolean().default(false),
1225
+ tags: z.array(tagSchema).optional(),
1171
1226
  media: mediaCollection.optional(),
1172
1227
  created_at: z.string(),
1173
1228
  updated_at: z.string(),
@@ -1283,17 +1338,90 @@ export const prePaidPackageSchema = z.object({
1283
1338
  .optional(),
1284
1339
  });
1285
1340
 
1341
+ // A billing line-item (flight charge, landing fee, membership fee, refund,
1342
+ // …) attached to a flight log / booking / membership via the polymorphic
1343
+ // (model_type, model_id) pair, and to an invoice once released. NOT a
1344
+ // catalog entry — rebuilt field-by-field against ExpenseItemResource.php;
1345
+ // the previous shape (name/category/active) modeled a company catalog row,
1346
+ // which ExpenseItem has never had.
1347
+ export const prePaidPackageAssignmentSchema = z.object({
1348
+ id: z.number(),
1349
+ flight_log_id: z.number().nullable().optional(),
1350
+ expense_item_id: z.number().nullable().optional(),
1351
+ pre_paid_package_id: z.number().nullable().optional(),
1352
+ gross_total: zPhpMoneyObject.nullable().optional(),
1353
+ created_at: z.string().nullable().optional(),
1354
+ updated_at: z.string().nullable().optional(),
1355
+ });
1356
+
1286
1357
  export const expenseItemSchema = z.object({
1287
- type: z.literal("expenseItem"),
1358
+ // ExpenseItemResource never emits a `type` key; kept optional for
1359
+ // consistency with sibling schemas' discriminated-union literal.
1360
+ type: z.literal("expenseItem").optional(),
1288
1361
  id: z.number(),
1362
+ date: z.string(),
1363
+ model_id: z.number(),
1364
+ model_type: z.string(),
1365
+ identifier: z.string().nullable(),
1366
+ // Provenance: the `prices` row this item was generated from. `price_id` is
1367
+ // always present (nullable FK); `price` only when the relation is
1368
+ // eager-loaded.
1369
+ price_id: z.number().nullable(),
1370
+ price: priceSchema.optional(),
1371
+ description: z.string(),
1372
+ description_lines: z.array(z.any()).nullable(),
1373
+ // Resource emits a trimmed decimal string (trailing zeros/dot stripped),
1374
+ // not a number.
1375
+ quantity: z.string(),
1376
+ discount: z.number(),
1377
+ applied_discounts: z.array(z.any()).nullable(),
1378
+ status: z.string(),
1379
+ is_system_controlled: z.boolean(),
1380
+ deferred: z.boolean(),
1381
+
1382
+ invoice_id: z.number().nullable(),
1289
1383
  company_id: z.number(),
1290
- company: companySchema.optional(),
1291
- name: z.string(),
1292
- description: z.string().nullable().optional(),
1293
- category: z.string().nullable().optional(),
1294
- active: z.boolean().default(true),
1384
+ membership_id: z.number(),
1385
+ billing_profile_id: z.number().nullable(),
1386
+
1387
+ gross_total_before_vouchers: zPhpMoneyObject,
1388
+ gross_total: zPhpMoneyObject,
1389
+ net_total: zPhpMoneyObject,
1390
+ tax_total: zPhpMoneyObject,
1391
+ // number_format(…, 2) string, not a number.
1392
+ tax_percentage: z.string(),
1393
+ billable: z.boolean(),
1394
+ meta: z.record(z.string(), z.any()).nullable(),
1395
+ input_value: z.number().nullable(),
1396
+ net_gross: zNetGross,
1397
+
1398
+ refunds_expense_item_id: z.number().nullable(),
1399
+
1295
1400
  created_at: z.string(),
1296
1401
  updated_at: z.string(),
1402
+
1403
+ // Lazy + explicitly typed as ZodTypeAny (so `invoice` infers as `any`
1404
+ // rather than the full Invoice shape): invoiceSchema is large (60+ fields
1405
+ // incl. media/activities), and this field is additionally reached from
1406
+ // courseEnrollmentSchema/bookingSchema/flightLogSchema via their own lazy
1407
+ // expenseItems refs — inlining the full inferred type here blows past TS's
1408
+ // type-serialization size limit (TS7056: "inferred type … exceeds the
1409
+ // maximum length"). No true cycle: invoiceSchema does not reference
1410
+ // expenseItemSchema back.
1411
+ invoice: z.lazy((): z.ZodTypeAny => invoiceSchema).optional(),
1412
+ membership: baseMembershipSchema.optional(),
1413
+ prePaidPackageAssignments: z
1414
+ .array(prePaidPackageAssignmentSchema)
1415
+ .optional(),
1416
+ // Loose record on purpose: gates are policy-reflected server-side and grow
1417
+ // over time — same convention as companySchema/invoiceSchema.
1418
+ can: z.record(z.string(), z.boolean().nullable()).optional(),
1419
+
1420
+ // Set true only by InvoiceItemSnapshotResource — the immutable,
1421
+ // frozen-at-release line-item shape reused for released/cancelled
1422
+ // invoices (same JSON shape as ExpenseItemResource by design). Absent on
1423
+ // a live ExpenseItemResource payload.
1424
+ is_snapshot: z.boolean().optional(),
1297
1425
  });
1298
1426
 
1299
1427
  export const waypointSchema = z.object({
package/src/types/ai.ts CHANGED
@@ -58,6 +58,7 @@ export interface LogbookFlightLogPayload {
58
58
  low_approaches: number;
59
59
  crew_assignments: { membership_id: number; role: string }[];
60
60
  membership_id: number | null;
61
+ billing_profile_id?: number | null;
61
62
  }
62
63
 
63
64
  export interface LogbookSuggestionMeta {
@@ -129,11 +129,26 @@ export type Event = z.infer<typeof ModelSchemas.eventSchema>;
129
129
  export type Ticket = z.infer<typeof ModelSchemas.ticketSchema>;
130
130
  export type Post = z.infer<typeof ModelSchemas.postSchema>;
131
131
 
132
+ /**
133
+ * Full-replacement tag sync entry — `id` marks an existing tag (wins over
134
+ * name matching), fresh tags carry only name (+ optional color) and are
135
+ * created server-side on save.
136
+ */
137
+ export interface TagSyncEntry {
138
+ id?: number | null;
139
+ name: string;
140
+ color?: string | null;
141
+ }
142
+
132
143
  export interface CreatePostInput {
133
144
  title?: string | null;
134
145
  content: string;
135
146
  sticky?: boolean;
136
147
  public?: boolean;
148
+ comments_disabled?: boolean;
149
+ tags?: TagSyncEntry[];
150
+ /** Action flag, not persisted: notify company members on publish. */
151
+ notify?: boolean;
137
152
  }
138
153
 
139
154
  export interface UpdatePostInput {
@@ -141,6 +156,8 @@ export interface UpdatePostInput {
141
156
  content: string;
142
157
  sticky?: boolean;
143
158
  public?: boolean;
159
+ comments_disabled?: boolean;
160
+ tags?: TagSyncEntry[];
144
161
  }
145
162
 
146
163
  export interface CreateAirplaneIssueInput {