@learncard/helpers 1.2.8 → 1.2.10

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/dist/helpers.d.ts CHANGED
@@ -634,6 +634,67 @@ export type DeepPartial<T> = T extends Date ? T : T extends object ? {
634
634
  } : T;
635
635
  export declare const isNotUndefined: <T>(value: T | null | undefined) => value is T;
636
636
  export declare const filterUndefined: <T>(arr: (T | null | undefined)[]) => T[];
637
+ /**
638
+ * App installation age restriction logic shared between frontend and backend.
639
+ *
640
+ * This helper evaluates whether a user can install an app based on:
641
+ * - Hard age blocks (min_age) - always blocks, even with guardian approval
642
+ * - Soft age restrictions (age_rating) - can be approved by guardian for child profiles
643
+ * - Contract requirements - require guardian approval for child profiles
644
+ */
645
+ export type AppInstallCheckResult = {
646
+ action: "proceed";
647
+ } | {
648
+ action: "hard_blocked";
649
+ reason: string;
650
+ } | {
651
+ action: "require_dob";
652
+ reason: string;
653
+ } | {
654
+ action: "require_guardian_approval";
655
+ reason: string;
656
+ };
657
+ export type AppInstallCheckInput = {
658
+ /** Whether the user is a child profile (managed account) */
659
+ isChildProfile: boolean;
660
+ /** User's age calculated from DOB, null if DOB is unknown */
661
+ userAge: number | null;
662
+ /** Hard minimum age requirement (blocks completely, no guardian override) */
663
+ minAge?: number;
664
+ /** Soft age rating string (e.g., '4+', '9+', '12+', '17+') */
665
+ ageRating?: string;
666
+ /** Whether the app has an associated consent contract */
667
+ hasContract: boolean;
668
+ /** Whether guardian approval has already been obtained (for backend validation) */
669
+ hasGuardianApproval?: boolean;
670
+ };
671
+ /**
672
+ * Evaluates whether a user can install an app based on age restrictions and profile type.
673
+ *
674
+ * Cases:
675
+ * 1. Hard block: min_age violation (blocks completely, cannot be overridden)
676
+ * 2. Child profile scenarios:
677
+ * 2a. Child age unknown → require DOB entry (with guardian approval)
678
+ * 2b. No age rating specified → require guardian approval
679
+ * 2c. Child too young for age rating → require guardian approval
680
+ * 2d. App has a contract → require guardian approval
681
+ * 2e. Child old enough, no contract → proceed without guardian
682
+ * 3. Adult user old enough → proceed
683
+ *
684
+ * @param input - The check parameters
685
+ * @returns The action to take and reason if blocked/requires approval
686
+ */
687
+ export declare const checkAppInstallEligibility: (input: AppInstallCheckInput) => AppInstallCheckResult;
688
+ /**
689
+ * Map age_rating strings to numeric minimum ages.
690
+ */
691
+ export declare const AGE_RATING_TO_MIN_AGE: Record<string, number>;
692
+ /**
693
+ * Calculate age in years from a date of birth string.
694
+ * @param dob - Date of birth as ISO string or parseable date string
695
+ * @returns Age in years, or null if the date is invalid
696
+ */
697
+ export declare const calculateAgeFromDob: (dob?: string | null) => number | null;
637
698
  /**
638
699
  * Determines whether or not a string is a valid hexadecimal string
639
700
  *
@@ -61,6 +61,7 @@ var require_types_cjs_development = __commonJS({
61
61
  AchievementValidator: /* @__PURE__ */ __name(() => AchievementValidator, "AchievementValidator"),
62
62
  AddTagInputValidator: /* @__PURE__ */ __name(() => AddTagInputValidator, "AddTagInputValidator"),
63
63
  AddressValidator: /* @__PURE__ */ __name(() => AddressValidator, "AddressValidator"),
64
+ AgeRatingValidator: /* @__PURE__ */ __name(() => AgeRatingValidator, "AgeRatingValidator"),
64
65
  AlignmentTargetTypeValidator: /* @__PURE__ */ __name(() => AlignmentTargetTypeValidator, "AlignmentTargetTypeValidator"),
65
66
  AlignmentValidator: /* @__PURE__ */ __name(() => AlignmentValidator, "AlignmentValidator"),
66
67
  AppBoostValidator: /* @__PURE__ */ __name(() => AppBoostValidator, "AppBoostValidator"),
@@ -74,6 +75,7 @@ var require_types_cjs_development = __commonJS({
74
75
  AuthGrantQueryValidator: /* @__PURE__ */ __name(() => AuthGrantQueryValidator, "AuthGrantQueryValidator"),
75
76
  AuthGrantStatusValidator: /* @__PURE__ */ __name(() => AuthGrantStatusValidator, "AuthGrantStatusValidator"),
76
77
  AuthGrantValidator: /* @__PURE__ */ __name(() => AuthGrantValidator, "AuthGrantValidator"),
78
+ AuthSessionError: /* @__PURE__ */ __name(() => AuthSessionError, "AuthSessionError"),
77
79
  AutoBoostConfigValidator: /* @__PURE__ */ __name(() => AutoBoostConfigValidator, "AutoBoostConfigValidator"),
78
80
  BoostPermissionsQueryValidator: /* @__PURE__ */ __name(() => BoostPermissionsQueryValidator, "BoostPermissionsQueryValidator"),
79
81
  BoostPermissionsValidator: /* @__PURE__ */ __name(() => BoostPermissionsValidator, "BoostPermissionsValidator"),
@@ -81,6 +83,8 @@ var require_types_cjs_development = __commonJS({
81
83
  BoostRecipientValidator: /* @__PURE__ */ __name(() => BoostRecipientValidator, "BoostRecipientValidator"),
82
84
  BoostRecipientWithChildrenValidator: /* @__PURE__ */ __name(() => BoostRecipientWithChildrenValidator, "BoostRecipientWithChildrenValidator"),
83
85
  BoostValidator: /* @__PURE__ */ __name(() => BoostValidator, "BoostValidator"),
86
+ CheckCredentialEventValidator: /* @__PURE__ */ __name(() => CheckCredentialEventValidator, "CheckCredentialEventValidator"),
87
+ CheckIssuanceStatusEventValidator: /* @__PURE__ */ __name(() => CheckIssuanceStatusEventValidator, "CheckIssuanceStatusEventValidator"),
84
88
  ClaimHookQueryValidator: /* @__PURE__ */ __name(() => ClaimHookQueryValidator, "ClaimHookQueryValidator"),
85
89
  ClaimHookTypeValidator: /* @__PURE__ */ __name(() => ClaimHookTypeValidator, "ClaimHookTypeValidator"),
86
90
  ClaimHookValidator: /* @__PURE__ */ __name(() => ClaimHookValidator, "ClaimHookValidator"),
@@ -122,6 +126,7 @@ var require_types_cjs_development = __commonJS({
122
126
  CredentialActivityValidator: /* @__PURE__ */ __name(() => CredentialActivityValidator, "CredentialActivityValidator"),
123
127
  CredentialActivityWithDetailsValidator: /* @__PURE__ */ __name(() => CredentialActivityWithDetailsValidator, "CredentialActivityWithDetailsValidator"),
124
128
  CredentialInfoValidator: /* @__PURE__ */ __name(() => CredentialInfoValidator, "CredentialInfoValidator"),
129
+ CredentialNameRefValidator: /* @__PURE__ */ __name(() => CredentialNameRefValidator, "CredentialNameRefValidator"),
125
130
  CredentialRecordValidator: /* @__PURE__ */ __name(() => CredentialRecordValidator, "CredentialRecordValidator"),
126
131
  CredentialSchemaValidator: /* @__PURE__ */ __name(() => CredentialSchemaValidator, "CredentialSchemaValidator"),
127
132
  CredentialStatusValidator: /* @__PURE__ */ __name(() => CredentialStatusValidator, "CredentialStatusValidator"),
@@ -143,6 +148,7 @@ var require_types_cjs_development = __commonJS({
143
148
  GetFullSkillTreeResultValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeResultValidator, "GetFullSkillTreeResultValidator"),
144
149
  GetSkillPathInputValidator: /* @__PURE__ */ __name(() => GetSkillPathInputValidator, "GetSkillPathInputValidator"),
145
150
  GetSkillPathResultValidator: /* @__PURE__ */ __name(() => GetSkillPathResultValidator, "GetSkillPathResultValidator"),
151
+ GetTemplateRecipientsEventValidator: /* @__PURE__ */ __name(() => GetTemplateRecipientsEventValidator, "GetTemplateRecipientsEventValidator"),
146
152
  IdentifierEntryValidator: /* @__PURE__ */ __name(() => IdentifierEntryValidator, "IdentifierEntryValidator"),
147
153
  IdentifierTypeValidator: /* @__PURE__ */ __name(() => IdentifierTypeValidator, "IdentifierTypeValidator"),
148
154
  IdentityObjectValidator: /* @__PURE__ */ __name(() => IdentityObjectValidator, "IdentityObjectValidator"),
@@ -14590,7 +14596,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14590
14596
  "PROFILE_PARENT_APPROVED",
14591
14597
  "APP_LISTING_SUBMITTED",
14592
14598
  "APP_LISTING_APPROVED",
14593
- "APP_LISTING_REJECTED"
14599
+ "APP_LISTING_REJECTED",
14600
+ "DEVICE_LINK_REQUEST"
14594
14601
  ]);
14595
14602
  var LCNNotificationMessageValidator = external_exports.object({
14596
14603
  title: external_exports.string().optional(),
@@ -14729,6 +14736,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14729
14736
  webhookUrl: external_exports.string().optional(),
14730
14737
  boostUri: external_exports.string().optional(),
14731
14738
  activityId: external_exports.string().optional(),
14739
+ integrationId: external_exports.string().optional(),
14732
14740
  signingAuthority: external_exports.object({
14733
14741
  endpoint: external_exports.string().optional(),
14734
14742
  name: external_exports.string().optional()
@@ -14830,8 +14838,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14830
14838
  claimUrl: external_exports.string().url().optional(),
14831
14839
  recipientDid: external_exports.string().optional()
14832
14840
  });
14841
+ var CredentialNameRefValidator = external_exports.object({ name: external_exports.string() }).passthrough();
14833
14842
  var ClaimInboxCredentialValidator = external_exports.object({
14834
- credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).describe("The credential to issue."),
14843
+ credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).or(CredentialNameRefValidator).describe("The credential to issue, or a { name } reference to resolve a boost template."),
14835
14844
  configuration: external_exports.object({
14836
14845
  publishableKey: external_exports.string(),
14837
14846
  signingAuthorityName: external_exports.string().optional(),
@@ -15114,6 +15123,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15114
15123
  "STANDARD",
15115
15124
  "DEMOTED"
15116
15125
  ]);
15126
+ var AgeRatingValidator = external_exports.enum(["4+", "9+", "12+", "17+"]);
15117
15127
  var AppStoreListingValidator = external_exports.object({
15118
15128
  listing_id: external_exports.string(),
15119
15129
  slug: external_exports.string().optional(),
@@ -15133,7 +15143,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15133
15143
  terms_url: external_exports.string().optional(),
15134
15144
  highlights: external_exports.array(external_exports.string()).optional(),
15135
15145
  screenshots: external_exports.array(external_exports.string()).optional(),
15136
- hero_background_color: external_exports.string().optional()
15146
+ hero_background_color: external_exports.string().optional(),
15147
+ min_age: external_exports.number().int().min(0).max(18).optional(),
15148
+ age_rating: AgeRatingValidator.optional()
15137
15149
  });
15138
15150
  var AppStoreListingCreateValidator = AppStoreListingValidator.omit({
15139
15151
  listing_id: true,
@@ -15161,9 +15173,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15161
15173
  var SendCredentialEventValidator = external_exports.object({
15162
15174
  type: external_exports.literal("send-credential"),
15163
15175
  templateAlias: external_exports.string(),
15164
- templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
15176
+ templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
15177
+ preventDuplicateClaim: external_exports.boolean().optional()
15178
+ });
15179
+ var CheckCredentialEventValidator = external_exports.object({
15180
+ type: external_exports.literal("check-credential"),
15181
+ templateAlias: external_exports.string().optional(),
15182
+ boostUri: external_exports.string().optional()
15183
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15184
+ message: "Exactly one of templateAlias or boostUri is required"
15185
+ });
15186
+ var CheckIssuanceStatusEventValidator = external_exports.object({
15187
+ type: external_exports.literal("check-issuance-status"),
15188
+ templateAlias: external_exports.string().optional(),
15189
+ boostUri: external_exports.string().optional(),
15190
+ recipient: external_exports.string()
15191
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15192
+ message: "Exactly one of templateAlias or boostUri is required"
15165
15193
  });
15166
- var AppEventValidator = external_exports.discriminatedUnion("type", [SendCredentialEventValidator]);
15194
+ var GetTemplateRecipientsEventValidator = external_exports.object({
15195
+ type: external_exports.literal("get-template-recipients"),
15196
+ templateAlias: external_exports.string().optional(),
15197
+ boostUri: external_exports.string().optional(),
15198
+ limit: external_exports.number().optional(),
15199
+ cursor: external_exports.string().optional()
15200
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15201
+ message: "Exactly one of templateAlias or boostUri is required"
15202
+ });
15203
+ var AppEventValidator = external_exports.discriminatedUnion("type", [
15204
+ SendCredentialEventValidator,
15205
+ CheckCredentialEventValidator,
15206
+ CheckIssuanceStatusEventValidator,
15207
+ GetTemplateRecipientsEventValidator
15208
+ ]);
15167
15209
  var AppEventInputValidator = external_exports.object({
15168
15210
  listingId: external_exports.string(),
15169
15211
  event: AppEventValidator
@@ -15226,6 +15268,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15226
15268
  failed: external_exports.number(),
15227
15269
  claimRate: external_exports.number()
15228
15270
  });
15271
+ var AuthSessionError = class extends Error {
15272
+ static {
15273
+ __name(this, "AuthSessionError");
15274
+ }
15275
+ constructor(message, reason) {
15276
+ super(message);
15277
+ this.reason = reason;
15278
+ this.name = "AuthSessionError";
15279
+ }
15280
+ static {
15281
+ __name2(this, "AuthSessionError");
15282
+ }
15283
+ };
15229
15284
  }
15230
15285
  });
15231
15286
 
@@ -16056,6 +16111,90 @@ var ImageWithLoadingStateValdator = z.object({
16056
16111
  var isNotUndefined = /* @__PURE__ */ __name((value) => Boolean(value), "isNotUndefined");
16057
16112
  var filterUndefined = /* @__PURE__ */ __name((arr) => arr.filter(isNotUndefined), "filterUndefined");
16058
16113
 
16114
+ // src/app-install/index.ts
16115
+ var checkAppInstallEligibility = /* @__PURE__ */ __name((input) => {
16116
+ const {
16117
+ isChildProfile,
16118
+ userAge,
16119
+ minAge,
16120
+ ageRating,
16121
+ hasContract,
16122
+ hasGuardianApproval = false
16123
+ } = input;
16124
+ const ageRatingMinAge = getAgeRatingMinAge(ageRating);
16125
+ const isHardBlocked = userAge !== null && minAge !== void 0 && minAge > 0 && userAge < minAge;
16126
+ if (isHardBlocked) {
16127
+ return {
16128
+ action: "hard_blocked",
16129
+ reason: `User does not meet the minimum age requirement of ${minAge}`
16130
+ };
16131
+ }
16132
+ if (isChildProfile) {
16133
+ const noAgeRating = ageRatingMinAge === 0;
16134
+ const childAgeUnknown = userAge === null;
16135
+ const childTooYoung = userAge !== null && userAge < ageRatingMinAge;
16136
+ if (childAgeUnknown) {
16137
+ if (hasGuardianApproval) {
16138
+ return { action: "proceed" };
16139
+ }
16140
+ return {
16141
+ action: "require_dob",
16142
+ reason: "Child profile age is unknown and must be verified by guardian"
16143
+ };
16144
+ }
16145
+ if (noAgeRating) {
16146
+ if (hasGuardianApproval) {
16147
+ return { action: "proceed" };
16148
+ }
16149
+ return {
16150
+ action: "require_guardian_approval",
16151
+ reason: "App has no age rating; guardian approval required for child profiles"
16152
+ };
16153
+ }
16154
+ if (childTooYoung) {
16155
+ if (hasGuardianApproval) {
16156
+ return { action: "proceed" };
16157
+ }
16158
+ return {
16159
+ action: "require_guardian_approval",
16160
+ reason: `Child is under the age rating of ${ageRatingMinAge}+; guardian approval required`
16161
+ };
16162
+ }
16163
+ if (hasContract) {
16164
+ if (hasGuardianApproval) {
16165
+ return { action: "proceed" };
16166
+ }
16167
+ return {
16168
+ action: "require_guardian_approval",
16169
+ reason: "App requires consent to a contract; guardian approval required for child profiles"
16170
+ };
16171
+ }
16172
+ }
16173
+ return { action: "proceed" };
16174
+ }, "checkAppInstallEligibility");
16175
+ var AGE_RATING_TO_MIN_AGE = {
16176
+ "4+": 4,
16177
+ "9+": 9,
16178
+ "12+": 12,
16179
+ "17+": 17
16180
+ };
16181
+ var getAgeRatingMinAge = /* @__PURE__ */ __name((ageRating) => {
16182
+ if (!ageRating) return 0;
16183
+ return AGE_RATING_TO_MIN_AGE[ageRating] ?? 0;
16184
+ }, "getAgeRatingMinAge");
16185
+ var calculateAgeFromDob = /* @__PURE__ */ __name((dob) => {
16186
+ if (!dob) return null;
16187
+ const birthDate = new Date(dob);
16188
+ if (isNaN(birthDate.getTime())) return null;
16189
+ const today = /* @__PURE__ */ new Date();
16190
+ let age = today.getFullYear() - birthDate.getFullYear();
16191
+ const monthDiff = today.getMonth() - birthDate.getMonth();
16192
+ if (monthDiff < 0 || monthDiff === 0 && today.getDate() < birthDate.getDate()) {
16193
+ age--;
16194
+ }
16195
+ return age;
16196
+ }, "calculateAgeFromDob");
16197
+
16059
16198
  // src/index.ts
16060
16199
  var isHex = /* @__PURE__ */ __name((str) => /^[0-9a-f]+$/i.test(str), "isHex");
16061
16200
  var isEncrypted = /* @__PURE__ */ __name((item) => {
@@ -16105,13 +16244,16 @@ var isAppDidWeb = /* @__PURE__ */ __name((did) => {
16105
16244
  return LCN_APP_DID_WEB_REGEX.test(did);
16106
16245
  }, "isAppDidWeb");
16107
16246
  export {
16247
+ AGE_RATING_TO_MIN_AGE,
16108
16248
  DEFAULT_RESOLUTIONS,
16109
16249
  ImageResizingValidator,
16110
16250
  ImageUploadingValidator,
16111
16251
  ImageWithLoadingStateValdator,
16112
16252
  RegExpTransformer,
16253
+ calculateAgeFromDob,
16113
16254
  capitalizeFirstLetter,
16114
16255
  changeQuality4 as changeQuality,
16256
+ checkAppInstallEligibility,
16115
16257
  curriedArraySlice,
16116
16258
  curriedInnerImmerOuterImmer,
16117
16259
  curriedInnerImmerOuterReact,