@learncard/helpers 1.2.9 → 1.2.11

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
  *
@@ -75,6 +75,7 @@ var require_types_cjs_development = __commonJS({
75
75
  AuthGrantQueryValidator: /* @__PURE__ */ __name(() => AuthGrantQueryValidator, "AuthGrantQueryValidator"),
76
76
  AuthGrantStatusValidator: /* @__PURE__ */ __name(() => AuthGrantStatusValidator, "AuthGrantStatusValidator"),
77
77
  AuthGrantValidator: /* @__PURE__ */ __name(() => AuthGrantValidator, "AuthGrantValidator"),
78
+ AuthSessionError: /* @__PURE__ */ __name(() => AuthSessionError, "AuthSessionError"),
78
79
  AutoBoostConfigValidator: /* @__PURE__ */ __name(() => AutoBoostConfigValidator, "AutoBoostConfigValidator"),
79
80
  BoostPermissionsQueryValidator: /* @__PURE__ */ __name(() => BoostPermissionsQueryValidator, "BoostPermissionsQueryValidator"),
80
81
  BoostPermissionsValidator: /* @__PURE__ */ __name(() => BoostPermissionsValidator, "BoostPermissionsValidator"),
@@ -82,6 +83,8 @@ var require_types_cjs_development = __commonJS({
82
83
  BoostRecipientValidator: /* @__PURE__ */ __name(() => BoostRecipientValidator, "BoostRecipientValidator"),
83
84
  BoostRecipientWithChildrenValidator: /* @__PURE__ */ __name(() => BoostRecipientWithChildrenValidator, "BoostRecipientWithChildrenValidator"),
84
85
  BoostValidator: /* @__PURE__ */ __name(() => BoostValidator, "BoostValidator"),
86
+ CheckCredentialEventValidator: /* @__PURE__ */ __name(() => CheckCredentialEventValidator, "CheckCredentialEventValidator"),
87
+ CheckIssuanceStatusEventValidator: /* @__PURE__ */ __name(() => CheckIssuanceStatusEventValidator, "CheckIssuanceStatusEventValidator"),
85
88
  ClaimHookQueryValidator: /* @__PURE__ */ __name(() => ClaimHookQueryValidator, "ClaimHookQueryValidator"),
86
89
  ClaimHookTypeValidator: /* @__PURE__ */ __name(() => ClaimHookTypeValidator, "ClaimHookTypeValidator"),
87
90
  ClaimHookValidator: /* @__PURE__ */ __name(() => ClaimHookValidator, "ClaimHookValidator"),
@@ -123,6 +126,7 @@ var require_types_cjs_development = __commonJS({
123
126
  CredentialActivityValidator: /* @__PURE__ */ __name(() => CredentialActivityValidator, "CredentialActivityValidator"),
124
127
  CredentialActivityWithDetailsValidator: /* @__PURE__ */ __name(() => CredentialActivityWithDetailsValidator, "CredentialActivityWithDetailsValidator"),
125
128
  CredentialInfoValidator: /* @__PURE__ */ __name(() => CredentialInfoValidator, "CredentialInfoValidator"),
129
+ CredentialNameRefValidator: /* @__PURE__ */ __name(() => CredentialNameRefValidator, "CredentialNameRefValidator"),
126
130
  CredentialRecordValidator: /* @__PURE__ */ __name(() => CredentialRecordValidator, "CredentialRecordValidator"),
127
131
  CredentialSchemaValidator: /* @__PURE__ */ __name(() => CredentialSchemaValidator, "CredentialSchemaValidator"),
128
132
  CredentialStatusValidator: /* @__PURE__ */ __name(() => CredentialStatusValidator, "CredentialStatusValidator"),
@@ -144,6 +148,7 @@ var require_types_cjs_development = __commonJS({
144
148
  GetFullSkillTreeResultValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeResultValidator, "GetFullSkillTreeResultValidator"),
145
149
  GetSkillPathInputValidator: /* @__PURE__ */ __name(() => GetSkillPathInputValidator, "GetSkillPathInputValidator"),
146
150
  GetSkillPathResultValidator: /* @__PURE__ */ __name(() => GetSkillPathResultValidator, "GetSkillPathResultValidator"),
151
+ GetTemplateRecipientsEventValidator: /* @__PURE__ */ __name(() => GetTemplateRecipientsEventValidator, "GetTemplateRecipientsEventValidator"),
147
152
  IdentifierEntryValidator: /* @__PURE__ */ __name(() => IdentifierEntryValidator, "IdentifierEntryValidator"),
148
153
  IdentifierTypeValidator: /* @__PURE__ */ __name(() => IdentifierTypeValidator, "IdentifierTypeValidator"),
149
154
  IdentityObjectValidator: /* @__PURE__ */ __name(() => IdentityObjectValidator, "IdentityObjectValidator"),
@@ -14591,7 +14596,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14591
14596
  "PROFILE_PARENT_APPROVED",
14592
14597
  "APP_LISTING_SUBMITTED",
14593
14598
  "APP_LISTING_APPROVED",
14594
- "APP_LISTING_REJECTED"
14599
+ "APP_LISTING_REJECTED",
14600
+ "APP_LISTING_WITHDRAWN",
14601
+ "DEVICE_LINK_REQUEST"
14595
14602
  ]);
14596
14603
  var LCNNotificationMessageValidator = external_exports.object({
14597
14604
  title: external_exports.string().optional(),
@@ -14730,6 +14737,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14730
14737
  webhookUrl: external_exports.string().optional(),
14731
14738
  boostUri: external_exports.string().optional(),
14732
14739
  activityId: external_exports.string().optional(),
14740
+ integrationId: external_exports.string().optional(),
14733
14741
  signingAuthority: external_exports.object({
14734
14742
  endpoint: external_exports.string().optional(),
14735
14743
  name: external_exports.string().optional()
@@ -14831,8 +14839,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14831
14839
  claimUrl: external_exports.string().url().optional(),
14832
14840
  recipientDid: external_exports.string().optional()
14833
14841
  });
14842
+ var CredentialNameRefValidator = external_exports.object({ name: external_exports.string() }).passthrough();
14834
14843
  var ClaimInboxCredentialValidator = external_exports.object({
14835
- credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).describe("The credential to issue."),
14844
+ credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).or(CredentialNameRefValidator).describe("The credential to issue, or a { name } reference to resolve a boost template."),
14836
14845
  configuration: external_exports.object({
14837
14846
  publishableKey: external_exports.string(),
14838
14847
  signingAuthorityName: external_exports.string().optional(),
@@ -15165,9 +15174,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15165
15174
  var SendCredentialEventValidator = external_exports.object({
15166
15175
  type: external_exports.literal("send-credential"),
15167
15176
  templateAlias: external_exports.string(),
15168
- templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
15177
+ templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
15178
+ preventDuplicateClaim: external_exports.boolean().optional()
15179
+ });
15180
+ var CheckCredentialEventValidator = external_exports.object({
15181
+ type: external_exports.literal("check-credential"),
15182
+ templateAlias: external_exports.string().optional(),
15183
+ boostUri: external_exports.string().optional()
15184
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15185
+ message: "Exactly one of templateAlias or boostUri is required"
15186
+ });
15187
+ var CheckIssuanceStatusEventValidator = external_exports.object({
15188
+ type: external_exports.literal("check-issuance-status"),
15189
+ templateAlias: external_exports.string().optional(),
15190
+ boostUri: external_exports.string().optional(),
15191
+ recipient: external_exports.string()
15192
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15193
+ message: "Exactly one of templateAlias or boostUri is required"
15169
15194
  });
15170
- var AppEventValidator = external_exports.discriminatedUnion("type", [SendCredentialEventValidator]);
15195
+ var GetTemplateRecipientsEventValidator = external_exports.object({
15196
+ type: external_exports.literal("get-template-recipients"),
15197
+ templateAlias: external_exports.string().optional(),
15198
+ boostUri: external_exports.string().optional(),
15199
+ limit: external_exports.number().optional(),
15200
+ cursor: external_exports.string().optional()
15201
+ }).refine((input) => Boolean(input.templateAlias) !== Boolean(input.boostUri), {
15202
+ message: "Exactly one of templateAlias or boostUri is required"
15203
+ });
15204
+ var AppEventValidator = external_exports.discriminatedUnion("type", [
15205
+ SendCredentialEventValidator,
15206
+ CheckCredentialEventValidator,
15207
+ CheckIssuanceStatusEventValidator,
15208
+ GetTemplateRecipientsEventValidator
15209
+ ]);
15171
15210
  var AppEventInputValidator = external_exports.object({
15172
15211
  listingId: external_exports.string(),
15173
15212
  event: AppEventValidator
@@ -15230,6 +15269,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15230
15269
  failed: external_exports.number(),
15231
15270
  claimRate: external_exports.number()
15232
15271
  });
15272
+ var AuthSessionError = class extends Error {
15273
+ static {
15274
+ __name(this, "AuthSessionError");
15275
+ }
15276
+ constructor(message, reason) {
15277
+ super(message);
15278
+ this.reason = reason;
15279
+ this.name = "AuthSessionError";
15280
+ }
15281
+ static {
15282
+ __name2(this, "AuthSessionError");
15283
+ }
15284
+ };
15233
15285
  }
15234
15286
  });
15235
15287
 
@@ -16060,6 +16112,90 @@ var ImageWithLoadingStateValdator = z.object({
16060
16112
  var isNotUndefined = /* @__PURE__ */ __name((value) => Boolean(value), "isNotUndefined");
16061
16113
  var filterUndefined = /* @__PURE__ */ __name((arr) => arr.filter(isNotUndefined), "filterUndefined");
16062
16114
 
16115
+ // src/app-install/index.ts
16116
+ var checkAppInstallEligibility = /* @__PURE__ */ __name((input) => {
16117
+ const {
16118
+ isChildProfile,
16119
+ userAge,
16120
+ minAge,
16121
+ ageRating,
16122
+ hasContract,
16123
+ hasGuardianApproval = false
16124
+ } = input;
16125
+ const ageRatingMinAge = getAgeRatingMinAge(ageRating);
16126
+ const isHardBlocked = userAge !== null && minAge !== void 0 && minAge > 0 && userAge < minAge;
16127
+ if (isHardBlocked) {
16128
+ return {
16129
+ action: "hard_blocked",
16130
+ reason: `User does not meet the minimum age requirement of ${minAge}`
16131
+ };
16132
+ }
16133
+ if (isChildProfile) {
16134
+ const noAgeRating = ageRatingMinAge === 0;
16135
+ const childAgeUnknown = userAge === null;
16136
+ const childTooYoung = userAge !== null && userAge < ageRatingMinAge;
16137
+ if (childAgeUnknown) {
16138
+ if (hasGuardianApproval) {
16139
+ return { action: "proceed" };
16140
+ }
16141
+ return {
16142
+ action: "require_dob",
16143
+ reason: "Child profile age is unknown and must be verified by guardian"
16144
+ };
16145
+ }
16146
+ if (noAgeRating) {
16147
+ if (hasGuardianApproval) {
16148
+ return { action: "proceed" };
16149
+ }
16150
+ return {
16151
+ action: "require_guardian_approval",
16152
+ reason: "App has no age rating; guardian approval required for child profiles"
16153
+ };
16154
+ }
16155
+ if (childTooYoung) {
16156
+ if (hasGuardianApproval) {
16157
+ return { action: "proceed" };
16158
+ }
16159
+ return {
16160
+ action: "require_guardian_approval",
16161
+ reason: `Child is under the age rating of ${ageRatingMinAge}+; guardian approval required`
16162
+ };
16163
+ }
16164
+ if (hasContract) {
16165
+ if (hasGuardianApproval) {
16166
+ return { action: "proceed" };
16167
+ }
16168
+ return {
16169
+ action: "require_guardian_approval",
16170
+ reason: "App requires consent to a contract; guardian approval required for child profiles"
16171
+ };
16172
+ }
16173
+ }
16174
+ return { action: "proceed" };
16175
+ }, "checkAppInstallEligibility");
16176
+ var AGE_RATING_TO_MIN_AGE = {
16177
+ "4+": 4,
16178
+ "9+": 9,
16179
+ "12+": 12,
16180
+ "17+": 17
16181
+ };
16182
+ var getAgeRatingMinAge = /* @__PURE__ */ __name((ageRating) => {
16183
+ if (!ageRating) return 0;
16184
+ return AGE_RATING_TO_MIN_AGE[ageRating] ?? 0;
16185
+ }, "getAgeRatingMinAge");
16186
+ var calculateAgeFromDob = /* @__PURE__ */ __name((dob) => {
16187
+ if (!dob) return null;
16188
+ const birthDate = new Date(dob);
16189
+ if (isNaN(birthDate.getTime())) return null;
16190
+ const today = /* @__PURE__ */ new Date();
16191
+ let age = today.getFullYear() - birthDate.getFullYear();
16192
+ const monthDiff = today.getMonth() - birthDate.getMonth();
16193
+ if (monthDiff < 0 || monthDiff === 0 && today.getDate() < birthDate.getDate()) {
16194
+ age--;
16195
+ }
16196
+ return age;
16197
+ }, "calculateAgeFromDob");
16198
+
16063
16199
  // src/index.ts
16064
16200
  var isHex = /* @__PURE__ */ __name((str) => /^[0-9a-f]+$/i.test(str), "isHex");
16065
16201
  var isEncrypted = /* @__PURE__ */ __name((item) => {
@@ -16109,13 +16245,16 @@ var isAppDidWeb = /* @__PURE__ */ __name((did) => {
16109
16245
  return LCN_APP_DID_WEB_REGEX.test(did);
16110
16246
  }, "isAppDidWeb");
16111
16247
  export {
16248
+ AGE_RATING_TO_MIN_AGE,
16112
16249
  DEFAULT_RESOLUTIONS,
16113
16250
  ImageResizingValidator,
16114
16251
  ImageUploadingValidator,
16115
16252
  ImageWithLoadingStateValdator,
16116
16253
  RegExpTransformer,
16254
+ calculateAgeFromDob,
16117
16255
  capitalizeFirstLetter,
16118
16256
  changeQuality4 as changeQuality,
16257
+ checkAppInstallEligibility,
16119
16258
  curriedArraySlice,
16120
16259
  curriedInnerImmerOuterImmer,
16121
16260
  curriedInnerImmerOuterReact,