@xapps-platform/xapp-manifest 0.2.0 → 0.2.2

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/index.js CHANGED
@@ -8086,6 +8086,168 @@ var xappManifestJsonSchema = {
8086
8086
  proxy_policy: { type: "object" }
8087
8087
  }
8088
8088
  },
8089
+ monetization: {
8090
+ type: "object",
8091
+ additionalProperties: false,
8092
+ properties: {
8093
+ products: {
8094
+ type: "array",
8095
+ maxItems: 200,
8096
+ items: {
8097
+ type: "object",
8098
+ required: ["slug", "product_family"],
8099
+ additionalProperties: false,
8100
+ properties: {
8101
+ slug: { type: "string", minLength: 1, maxLength: 100 },
8102
+ title: localizedTextSchema(200),
8103
+ description: localizedTextSchema(2e3),
8104
+ product_family: { type: "string", minLength: 1, maxLength: 100 },
8105
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8106
+ metadata: { type: "object" }
8107
+ }
8108
+ }
8109
+ },
8110
+ offerings: {
8111
+ type: "array",
8112
+ maxItems: 200,
8113
+ items: {
8114
+ type: "object",
8115
+ required: ["slug"],
8116
+ additionalProperties: false,
8117
+ properties: {
8118
+ slug: { type: "string", minLength: 1, maxLength: 100 },
8119
+ title: localizedTextSchema(200),
8120
+ description: localizedTextSchema(2e3),
8121
+ placement: { type: "string", minLength: 1, maxLength: 100 },
8122
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8123
+ targeting_rules: { type: "object" },
8124
+ metadata: { type: "object" }
8125
+ }
8126
+ }
8127
+ },
8128
+ packages: {
8129
+ type: "array",
8130
+ maxItems: 500,
8131
+ items: {
8132
+ type: "object",
8133
+ required: ["slug", "product_ref", "offering_ref"],
8134
+ additionalProperties: false,
8135
+ properties: {
8136
+ slug: { type: "string", minLength: 1, maxLength: 100 },
8137
+ product_ref: { type: "string", minLength: 1, maxLength: 100 },
8138
+ offering_ref: { type: "string", minLength: 1, maxLength: 100 },
8139
+ package_kind: {
8140
+ type: "string",
8141
+ enum: ["standard", "one_time_unlock", "credit_pack", "subscription"]
8142
+ },
8143
+ display_order: { type: "integer", minimum: 0, maximum: 1e6 },
8144
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8145
+ metadata: { type: "object" }
8146
+ }
8147
+ }
8148
+ },
8149
+ prices: {
8150
+ type: "array",
8151
+ maxItems: 1e3,
8152
+ items: {
8153
+ type: "object",
8154
+ required: ["slug", "package_ref", "currency", "amount"],
8155
+ additionalProperties: false,
8156
+ properties: {
8157
+ slug: { type: "string", minLength: 1, maxLength: 100 },
8158
+ package_ref: { type: "string", minLength: 1, maxLength: 100 },
8159
+ currency: { type: "string", minLength: 1, maxLength: 16 },
8160
+ amount: { type: "number", minimum: 0 },
8161
+ billing_period: { type: "string", enum: ["day", "week", "month", "year"] },
8162
+ billing_period_count: { type: "integer", minimum: 1, maximum: 1e6 },
8163
+ trial_policy: { type: "object" },
8164
+ intro_policy: { type: "object" },
8165
+ country_rules: { type: "object" },
8166
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8167
+ metadata: { type: "object" }
8168
+ }
8169
+ }
8170
+ },
8171
+ paywalls: {
8172
+ type: "array",
8173
+ maxItems: 200,
8174
+ items: {
8175
+ type: "object",
8176
+ required: ["slug", "offering_refs"],
8177
+ additionalProperties: false,
8178
+ properties: {
8179
+ slug: { type: "string", minLength: 1, maxLength: 100 },
8180
+ title: localizedTextSchema(200),
8181
+ description: localizedTextSchema(2e3),
8182
+ placement: { type: "string", minLength: 1, maxLength: 100 },
8183
+ offering_refs: {
8184
+ type: "array",
8185
+ minItems: 1,
8186
+ maxItems: 50,
8187
+ items: { type: "string", minLength: 1, maxLength: 100 }
8188
+ },
8189
+ package_refs: {
8190
+ type: "array",
8191
+ minItems: 1,
8192
+ maxItems: 200,
8193
+ items: { type: "string", minLength: 1, maxLength: 100 }
8194
+ },
8195
+ default_package_ref: { type: "string", minLength: 1, maxLength: 100 },
8196
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8197
+ targeting_rules: { type: "object" },
8198
+ metadata: { type: "object" }
8199
+ }
8200
+ }
8201
+ },
8202
+ usage_policies: {
8203
+ type: "array",
8204
+ maxItems: 200,
8205
+ items: {
8206
+ type: "object",
8207
+ required: ["tool_name", "credit_cost"],
8208
+ additionalProperties: false,
8209
+ properties: {
8210
+ tool_name: { type: "string", minLength: 1, maxLength: 100 },
8211
+ unit: { type: "string", minLength: 1, maxLength: 100 },
8212
+ credit_cost: { type: "number", minimum: 0 },
8213
+ status: { type: "string", enum: ["draft", "active", "archived"] },
8214
+ metadata: { type: "object" }
8215
+ }
8216
+ }
8217
+ },
8218
+ hooks: {
8219
+ type: "object",
8220
+ additionalProperties: false,
8221
+ properties: {
8222
+ after_payment_completed: {
8223
+ type: "object",
8224
+ additionalProperties: true,
8225
+ properties: {
8226
+ enabled: { type: "boolean" },
8227
+ guard_slug: { type: "string", minLength: 1, maxLength: 200 },
8228
+ guardSlug: { type: "string", minLength: 1, maxLength: 200 },
8229
+ invoice_ref: { type: "string", minLength: 1, maxLength: 200 },
8230
+ invoiceRef: { type: "string", minLength: 1, maxLength: 200 },
8231
+ by_payment_guard_ref: {
8232
+ type: "object",
8233
+ additionalProperties: {
8234
+ type: "object",
8235
+ additionalProperties: true
8236
+ }
8237
+ },
8238
+ byPaymentGuardRef: {
8239
+ type: "object",
8240
+ additionalProperties: {
8241
+ type: "object",
8242
+ additionalProperties: true
8243
+ }
8244
+ }
8245
+ }
8246
+ }
8247
+ }
8248
+ }
8249
+ }
8250
+ },
8089
8251
  payment_guard_definitions: {
8090
8252
  type: "array",
8091
8253
  maxItems: 50,
@@ -8317,6 +8479,23 @@ var validate = ajv.compile(xappManifestJsonSchema);
8317
8479
  function isRecord(value) {
8318
8480
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8319
8481
  }
8482
+ function readMonetizationEntries(value) {
8483
+ return Array.isArray(value) ? value : [];
8484
+ }
8485
+ function requireUniqueSlug(entries, label) {
8486
+ const slugs = /* @__PURE__ */ new Set();
8487
+ for (const entry of entries) {
8488
+ const slug = String(entry?.slug ?? "").trim();
8489
+ if (!slug) {
8490
+ throw Object.assign(new Error(`${label} entries require non-empty slug`), { status: 400 });
8491
+ }
8492
+ if (slugs.has(slug)) {
8493
+ throw Object.assign(new Error(`Duplicate ${label} slug: ${slug}`), { status: 400 });
8494
+ }
8495
+ slugs.add(slug);
8496
+ }
8497
+ return slugs;
8498
+ }
8320
8499
  function isTemplatedUrl(value) {
8321
8500
  return /^__[A-Z0-9_]+__(?:[/?#].*)?$/.test(String(value || "").trim());
8322
8501
  }
@@ -8733,6 +8912,13 @@ function parseXappManifest(input, options) {
8733
8912
  });
8734
8913
  }
8735
8914
  const manifest = input;
8915
+ const monetization = isRecord(manifest.monetization) ? manifest.monetization : null;
8916
+ const monetizationProducts = readMonetizationEntries(monetization?.products);
8917
+ const monetizationOfferings = readMonetizationEntries(monetization?.offerings);
8918
+ const monetizationPackages = readMonetizationEntries(monetization?.packages);
8919
+ const monetizationPrices = readMonetizationEntries(monetization?.prices);
8920
+ const monetizationPaywalls = readMonetizationEntries(monetization?.paywalls);
8921
+ const monetizationUsagePolicies = Array.isArray(monetization?.usage_policies) ? monetization.usage_policies : [];
8736
8922
  const paymentDefinitions = Array.isArray(manifest.payment_guard_definitions) ? manifest.payment_guard_definitions : [];
8737
8923
  const subjectProfileDefinitions = Array.isArray(manifest.subject_profile_guard_definitions) ? manifest.subject_profile_guard_definitions : [];
8738
8924
  const notificationDefinitions = Array.isArray(manifest.notification_definitions) ? manifest.notification_definitions : [];
@@ -8745,6 +8931,14 @@ function parseXappManifest(input, options) {
8745
8931
  const notificationTemplateNames = /* @__PURE__ */ new Set();
8746
8932
  const invoiceDefinitionNames = /* @__PURE__ */ new Set();
8747
8933
  const invoiceTemplateNames = /* @__PURE__ */ new Set();
8934
+ const monetizationProductSlugs = requireUniqueSlug(monetizationProducts, "monetization.products");
8935
+ const monetizationOfferingSlugs = requireUniqueSlug(
8936
+ monetizationOfferings,
8937
+ "monetization.offerings"
8938
+ );
8939
+ const monetizationPackageSlugs = requireUniqueSlug(monetizationPackages, "monetization.packages");
8940
+ requireUniqueSlug(monetizationPrices, "monetization.prices");
8941
+ requireUniqueSlug(monetizationPaywalls, "monetization.paywalls");
8748
8942
  for (const list of [
8749
8943
  [paymentDefinitions, paymentDefinitionNames, "payment_guard_definitions"],
8750
8944
  [subjectProfileDefinitions, subjectProfileDefinitionNames, "subject_profile_guard_definitions"],
@@ -8789,6 +8983,30 @@ function parseXappManifest(input, options) {
8789
8983
  const knownToolNames = new Set(
8790
8984
  (manifest.tools ?? []).map((tool) => String(tool.tool_name || "").trim())
8791
8985
  );
8986
+ const monetizationUsagePolicyTools = /* @__PURE__ */ new Set();
8987
+ for (const usagePolicy of monetizationUsagePolicies) {
8988
+ const toolName = String(usagePolicy?.tool_name ?? "").trim();
8989
+ const creditCost = Number(usagePolicy?.credit_cost);
8990
+ if (!knownToolNames.has(toolName)) {
8991
+ throw Object.assign(
8992
+ new Error(`monetization.usage_policies entry references unknown tool_name: ${toolName}`),
8993
+ { status: 400 }
8994
+ );
8995
+ }
8996
+ if (!Number.isFinite(creditCost)) {
8997
+ throw Object.assign(
8998
+ new Error(`monetization.usage_policies entry requires numeric credit_cost: ${toolName}`),
8999
+ { status: 400 }
9000
+ );
9001
+ }
9002
+ if (monetizationUsagePolicyTools.has(toolName)) {
9003
+ throw Object.assign(
9004
+ new Error(`monetization.usage_policies has duplicate tool_name: ${toolName}`),
9005
+ { status: 400 }
9006
+ );
9007
+ }
9008
+ monetizationUsagePolicyTools.add(toolName);
9009
+ }
8792
9010
  const referencedPaymentDefinitionNames = /* @__PURE__ */ new Set();
8793
9011
  const referencedSubjectProfileDefinitionNames = /* @__PURE__ */ new Set();
8794
9012
  const notificationDefinitionRegistry = buildNotificationDefinitionRegistry(
@@ -8839,6 +9057,133 @@ function parseXappManifest(input, options) {
8839
9057
  validateWidgetPublisherRuntimeMode(widget);
8840
9058
  validateWidgetPublisherBootstrapTransport(widget);
8841
9059
  }
9060
+ for (const monetizationPackage of monetizationPackages) {
9061
+ const productRef = String(monetizationPackage.product_ref ?? "").trim();
9062
+ if (!monetizationProductSlugs.has(productRef)) {
9063
+ throw Object.assign(
9064
+ new Error(
9065
+ `monetization.packages entry ${String(monetizationPackage.slug ?? "unknown")} references unknown product_ref: ${productRef}`
9066
+ ),
9067
+ { status: 400 }
9068
+ );
9069
+ }
9070
+ const offeringRef = String(monetizationPackage.offering_ref ?? "").trim();
9071
+ if (!monetizationOfferingSlugs.has(offeringRef)) {
9072
+ throw Object.assign(
9073
+ new Error(
9074
+ `monetization.packages entry ${String(monetizationPackage.slug ?? "unknown")} references unknown offering_ref: ${offeringRef}`
9075
+ ),
9076
+ { status: 400 }
9077
+ );
9078
+ }
9079
+ }
9080
+ for (const monetizationPrice of monetizationPrices) {
9081
+ const packageRef = String(monetizationPrice.package_ref ?? "").trim();
9082
+ if (!monetizationPackageSlugs.has(packageRef)) {
9083
+ throw Object.assign(
9084
+ new Error(
9085
+ `monetization.prices entry ${String(monetizationPrice.slug ?? "unknown")} references unknown package_ref: ${packageRef}`
9086
+ ),
9087
+ { status: 400 }
9088
+ );
9089
+ }
9090
+ }
9091
+ const packageOfferingBySlug = /* @__PURE__ */ new Map();
9092
+ for (const monetizationPackage of monetizationPackages) {
9093
+ const packageSlug = String(monetizationPackage.slug ?? "").trim();
9094
+ const offeringRef = String(monetizationPackage.offering_ref ?? "").trim();
9095
+ if (packageSlug && offeringRef) {
9096
+ packageOfferingBySlug.set(packageSlug, offeringRef);
9097
+ }
9098
+ }
9099
+ for (const monetizationPaywall of monetizationPaywalls) {
9100
+ const paywallSlug = String(monetizationPaywall.slug ?? "unknown").trim() || "unknown";
9101
+ const offeringRefs = Array.isArray(monetizationPaywall.offering_refs) ? monetizationPaywall.offering_refs.map((value) => String(value ?? "").trim()).filter(Boolean) : [];
9102
+ if (!offeringRefs.length) {
9103
+ throw Object.assign(
9104
+ new Error(`monetization.paywalls entry ${paywallSlug} requires at least one offering_ref`),
9105
+ { status: 400 }
9106
+ );
9107
+ }
9108
+ const offeringRefSet = /* @__PURE__ */ new Set();
9109
+ for (const offeringRef of offeringRefs) {
9110
+ if (!monetizationOfferingSlugs.has(offeringRef)) {
9111
+ throw Object.assign(
9112
+ new Error(
9113
+ `monetization.paywalls entry ${paywallSlug} references unknown offering_ref: ${offeringRef}`
9114
+ ),
9115
+ { status: 400 }
9116
+ );
9117
+ }
9118
+ if (offeringRefSet.has(offeringRef)) {
9119
+ throw Object.assign(
9120
+ new Error(
9121
+ `monetization.paywalls entry ${paywallSlug} has duplicate offering_ref: ${offeringRef}`
9122
+ ),
9123
+ { status: 400 }
9124
+ );
9125
+ }
9126
+ offeringRefSet.add(offeringRef);
9127
+ }
9128
+ const packageRefs = Array.isArray(monetizationPaywall.package_refs) ? monetizationPaywall.package_refs.map((value) => String(value ?? "").trim()).filter(Boolean) : [];
9129
+ const packageRefSet = /* @__PURE__ */ new Set();
9130
+ for (const packageRef of packageRefs) {
9131
+ if (!monetizationPackageSlugs.has(packageRef)) {
9132
+ throw Object.assign(
9133
+ new Error(
9134
+ `monetization.paywalls entry ${paywallSlug} references unknown package_ref: ${packageRef}`
9135
+ ),
9136
+ { status: 400 }
9137
+ );
9138
+ }
9139
+ if (packageRefSet.has(packageRef)) {
9140
+ throw Object.assign(
9141
+ new Error(
9142
+ `monetization.paywalls entry ${paywallSlug} has duplicate package_ref: ${packageRef}`
9143
+ ),
9144
+ { status: 400 }
9145
+ );
9146
+ }
9147
+ packageRefSet.add(packageRef);
9148
+ const packageOfferingRef = String(packageOfferingBySlug.get(packageRef) ?? "").trim();
9149
+ if (packageOfferingRef && !offeringRefSet.has(packageOfferingRef)) {
9150
+ throw Object.assign(
9151
+ new Error(
9152
+ `monetization.paywalls entry ${paywallSlug} package_ref ${packageRef} is not inside paywall offering_refs`
9153
+ ),
9154
+ { status: 400 }
9155
+ );
9156
+ }
9157
+ }
9158
+ const defaultPackageRef = String(monetizationPaywall.default_package_ref ?? "").trim();
9159
+ if (defaultPackageRef) {
9160
+ if (!monetizationPackageSlugs.has(defaultPackageRef)) {
9161
+ throw Object.assign(
9162
+ new Error(
9163
+ `monetization.paywalls entry ${paywallSlug} references unknown default_package_ref: ${defaultPackageRef}`
9164
+ ),
9165
+ { status: 400 }
9166
+ );
9167
+ }
9168
+ if (packageRefSet.size > 0 && !packageRefSet.has(defaultPackageRef)) {
9169
+ throw Object.assign(
9170
+ new Error(
9171
+ `monetization.paywalls entry ${paywallSlug} default_package_ref must also exist in package_refs when package_refs are declared`
9172
+ ),
9173
+ { status: 400 }
9174
+ );
9175
+ }
9176
+ const defaultOfferingRef = String(packageOfferingBySlug.get(defaultPackageRef) ?? "").trim();
9177
+ if (defaultOfferingRef && !offeringRefSet.has(defaultOfferingRef)) {
9178
+ throw Object.assign(
9179
+ new Error(
9180
+ `monetization.paywalls entry ${paywallSlug} default_package_ref ${defaultPackageRef} is not inside paywall offering_refs`
9181
+ ),
9182
+ { status: 400 }
9183
+ );
9184
+ }
9185
+ }
9186
+ }
8842
9187
  for (const tool of manifest.tools ?? []) {
8843
9188
  validateAdapterShape(tool);
8844
9189
  const policy = tool.dispatch_policy;