@learncard/helpers 1.2.9 → 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
  *
@@ -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,8 @@ 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
+ "DEVICE_LINK_REQUEST"
14595
14601
  ]);
14596
14602
  var LCNNotificationMessageValidator = external_exports.object({
14597
14603
  title: external_exports.string().optional(),
@@ -14730,6 +14736,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14730
14736
  webhookUrl: external_exports.string().optional(),
14731
14737
  boostUri: external_exports.string().optional(),
14732
14738
  activityId: external_exports.string().optional(),
14739
+ integrationId: external_exports.string().optional(),
14733
14740
  signingAuthority: external_exports.object({
14734
14741
  endpoint: external_exports.string().optional(),
14735
14742
  name: external_exports.string().optional()
@@ -14831,8 +14838,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14831
14838
  claimUrl: external_exports.string().url().optional(),
14832
14839
  recipientDid: external_exports.string().optional()
14833
14840
  });
14841
+ var CredentialNameRefValidator = external_exports.object({ name: external_exports.string() }).passthrough();
14834
14842
  var ClaimInboxCredentialValidator = external_exports.object({
14835
- 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."),
14836
14844
  configuration: external_exports.object({
14837
14845
  publishableKey: external_exports.string(),
14838
14846
  signingAuthorityName: external_exports.string().optional(),
@@ -15165,9 +15173,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15165
15173
  var SendCredentialEventValidator = external_exports.object({
15166
15174
  type: external_exports.literal("send-credential"),
15167
15175
  templateAlias: external_exports.string(),
15168
- 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"
15169
15193
  });
15170
- 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
+ ]);
15171
15209
  var AppEventInputValidator = external_exports.object({
15172
15210
  listingId: external_exports.string(),
15173
15211
  event: AppEventValidator
@@ -15230,6 +15268,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15230
15268
  failed: external_exports.number(),
15231
15269
  claimRate: external_exports.number()
15232
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
+ };
15233
15284
  }
15234
15285
  });
15235
15286
 
@@ -16060,6 +16111,90 @@ var ImageWithLoadingStateValdator = z.object({
16060
16111
  var isNotUndefined = /* @__PURE__ */ __name((value) => Boolean(value), "isNotUndefined");
16061
16112
  var filterUndefined = /* @__PURE__ */ __name((arr) => arr.filter(isNotUndefined), "filterUndefined");
16062
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
+
16063
16198
  // src/index.ts
16064
16199
  var isHex = /* @__PURE__ */ __name((str) => /^[0-9a-f]+$/i.test(str), "isHex");
16065
16200
  var isEncrypted = /* @__PURE__ */ __name((item) => {
@@ -16109,13 +16244,16 @@ var isAppDidWeb = /* @__PURE__ */ __name((did) => {
16109
16244
  return LCN_APP_DID_WEB_REGEX.test(did);
16110
16245
  }, "isAppDidWeb");
16111
16246
  export {
16247
+ AGE_RATING_TO_MIN_AGE,
16112
16248
  DEFAULT_RESOLUTIONS,
16113
16249
  ImageResizingValidator,
16114
16250
  ImageUploadingValidator,
16115
16251
  ImageWithLoadingStateValdator,
16116
16252
  RegExpTransformer,
16253
+ calculateAgeFromDob,
16117
16254
  capitalizeFirstLetter,
16118
16255
  changeQuality4 as changeQuality,
16256
+ checkAppInstallEligibility,
16119
16257
  curriedArraySlice,
16120
16258
  curriedInnerImmerOuterImmer,
16121
16259
  curriedInnerImmerOuterReact,