@grupolapa/desarrollos-sdk 0.1.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.
Files changed (50) hide show
  1. package/README.md +154 -0
  2. package/dist/bundle-payment-schedule.calc.d.ts +2 -0
  3. package/dist/bundle-payment-schedule.calc.js +160 -0
  4. package/dist/calculate-quote.d.ts +2 -0
  5. package/dist/calculate-quote.js +382 -0
  6. package/dist/corporate-actions.d.ts +95 -0
  7. package/dist/corporate-actions.js +87 -0
  8. package/dist/corporate-rent.d.ts +5 -0
  9. package/dist/corporate-rent.js +43 -0
  10. package/dist/delivery-date.d.ts +6 -0
  11. package/dist/delivery-date.js +22 -0
  12. package/dist/entrada.d.ts +111 -0
  13. package/dist/entrada.js +139 -0
  14. package/dist/format.d.ts +7 -0
  15. package/dist/format.js +37 -0
  16. package/dist/http.d.ts +29 -0
  17. package/dist/http.js +123 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.js +22 -0
  20. package/dist/installment-allocation.d.ts +30 -0
  21. package/dist/installment-allocation.js +69 -0
  22. package/dist/inventory-availability.d.ts +10 -0
  23. package/dist/inventory-availability.js +105 -0
  24. package/dist/live-inventory-availability.d.ts +16 -0
  25. package/dist/live-inventory-availability.js +88 -0
  26. package/dist/payment-schemes.d.ts +22 -0
  27. package/dist/payment-schemes.js +99 -0
  28. package/dist/post-delivery-value.d.ts +3 -0
  29. package/dist/post-delivery-value.js +61 -0
  30. package/dist/product-types.d.ts +16 -0
  31. package/dist/product-types.js +82 -0
  32. package/dist/query-state.d.ts +5 -0
  33. package/dist/query-state.js +157 -0
  34. package/dist/quote-outcome-graph.d.ts +31 -0
  35. package/dist/quote-outcome-graph.js +113 -0
  36. package/dist/quote.d.ts +19 -0
  37. package/dist/quote.js +18 -0
  38. package/dist/selection-state.d.ts +34 -0
  39. package/dist/selection-state.js +156 -0
  40. package/dist/server.d.ts +17 -0
  41. package/dist/server.js +18 -0
  42. package/dist/sitemap.d.ts +22 -0
  43. package/dist/sitemap.js +45 -0
  44. package/dist/social-quote.d.ts +8 -0
  45. package/dist/social-quote.js +51 -0
  46. package/dist/tax.d.ts +5 -0
  47. package/dist/tax.js +11 -0
  48. package/dist/types.d.ts +511 -0
  49. package/dist/types.js +1 -0
  50. package/package.json +51 -0
@@ -0,0 +1,111 @@
1
+ import type { InventoryUnit } from "./types.js";
2
+ export declare const ENTRADA_BOOKING_AMOUNT_MXN = 10000;
3
+ export declare const APARTMENT_TOTAL_MONTHS = 36;
4
+ export declare const ENTRADA_ACTION_SCHEMES: readonly [{
5
+ readonly id: "contado";
6
+ readonly description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.";
7
+ readonly engancheRatio: 1;
8
+ readonly label: "Contado";
9
+ readonly monthlyCount: 0;
10
+ readonly monthlyRatio: 0;
11
+ readonly tag: "Máximo rendimiento";
12
+ readonly tasaObra: 0.08;
13
+ readonly tasaObraLabel: "8%";
14
+ }, {
15
+ readonly id: "medio";
16
+ readonly description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.";
17
+ readonly engancheRatio: number;
18
+ readonly label: "Financiado 5 meses";
19
+ readonly monthlyCount: 5;
20
+ readonly monthlyRatio: number;
21
+ readonly tag: "Balance";
22
+ readonly tasaObra: 0.06;
23
+ readonly tasaObraLabel: "6%";
24
+ }, {
25
+ readonly id: "amplio";
26
+ readonly description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.";
27
+ readonly engancheRatio: number;
28
+ readonly label: "Financiado 13 meses";
29
+ readonly monthlyCount: 13;
30
+ readonly monthlyRatio: number;
31
+ readonly tag: "Más accesible";
32
+ readonly tasaObra: 0.04;
33
+ readonly tasaObraLabel: "4%";
34
+ }];
35
+ type EntradaActionScheme = (typeof ENTRADA_ACTION_SCHEMES)[number];
36
+ export type EntradaActionSchemeId = EntradaActionScheme["id"];
37
+ export declare function getApartmentPartValueMXN(unit: InventoryUnit): number;
38
+ export declare function buildApartmentPlan(unit: InventoryUnit, requestedParts: number, rentalRate?: number): {
39
+ deliveryValueMXN: number;
40
+ engancheMXN: number;
41
+ fullLotValueMXN: number;
42
+ monthlyPaymentMXN: number;
43
+ numParts: number;
44
+ rentaMensualMXN: number;
45
+ saldoFinalMXN: number;
46
+ totalMonths: number;
47
+ totalToPayMXN: number;
48
+ yourValueMXN: number;
49
+ };
50
+ export declare function getCorporatePricePerActionMXN(unit: InventoryUnit, totalShares: number): number;
51
+ export declare function getCorporateM2PerAction(unit: InventoryUnit, totalShares: number): number;
52
+ export declare function buildCorporatePlan(args: {
53
+ inflationRate: number;
54
+ numActions: number;
55
+ requestedSchemeId: EntradaActionSchemeId | string | null | undefined;
56
+ rentalRate: number;
57
+ totalShares: number;
58
+ unit: InventoryUnit;
59
+ }): {
60
+ capitalMXN: number;
61
+ engancheMXN: number;
62
+ m2: number;
63
+ monthlyCount: 0 | 5 | 13;
64
+ monthlyPaymentMXN: number;
65
+ numActions: number;
66
+ operationMonthlyReturnMXN: number;
67
+ operationAnnualReturnNetMXN: number;
68
+ operationMonthlyReturnNetMXN: number;
69
+ operationReturnHorizonNetMXN: number;
70
+ obraReturnMonthlyMXN: number;
71
+ obraReturnMonthlyNetMXN: number;
72
+ obraReturnNetMXN: number;
73
+ patrimonioTotalMXN: number;
74
+ plusvaliaGainMXN: number;
75
+ pricePerActionMXN: number;
76
+ projectedAssetValueMXN: number;
77
+ scheme: {
78
+ readonly id: "contado";
79
+ readonly description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.";
80
+ readonly engancheRatio: 1;
81
+ readonly label: "Contado";
82
+ readonly monthlyCount: 0;
83
+ readonly monthlyRatio: 0;
84
+ readonly tag: "Máximo rendimiento";
85
+ readonly tasaObra: 0.08;
86
+ readonly tasaObraLabel: "8%";
87
+ } | {
88
+ readonly id: "medio";
89
+ readonly description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.";
90
+ readonly engancheRatio: number;
91
+ readonly label: "Financiado 5 meses";
92
+ readonly monthlyCount: 5;
93
+ readonly monthlyRatio: number;
94
+ readonly tag: "Balance";
95
+ readonly tasaObra: 0.06;
96
+ readonly tasaObraLabel: "6%";
97
+ } | {
98
+ readonly id: "amplio";
99
+ readonly description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.";
100
+ readonly engancheRatio: number;
101
+ readonly label: "Financiado 13 meses";
102
+ readonly monthlyCount: 13;
103
+ readonly monthlyRatio: number;
104
+ readonly tag: "Más accesible";
105
+ readonly tasaObra: 0.04;
106
+ readonly tasaObraLabel: "4%";
107
+ };
108
+ totalMonths: 0 | 5 | 13;
109
+ totalPaidMXN: number;
110
+ };
111
+ export {};
@@ -0,0 +1,139 @@
1
+ import { getUnitListPriceMXN } from "./payment-schemes.js";
2
+ import { getEscalatedAmountOverHorizonMXN } from "./corporate-rent.js";
3
+ const APARTMENT_TOTAL_PARTS = 6;
4
+ const APARTMENT_COPROP_PRICE_MULTIPLIER = 1.05;
5
+ const APARTMENT_ENGANCHE_RATIO = 60000 / 325000;
6
+ const APARTMENT_SALDO_FINAL_RATIO = 60000 / 325000;
7
+ const APARTMENT_MENSUALIDAD_RATIO = 6000 / 325000;
8
+ const APARTMENT_TOTAL_RENT_RATIO = 18000 / 1950000;
9
+ const APARTMENT_DELIVERY_VALUE_RATIO = 2900000 / 1950000;
10
+ const CORPORATE_PLUSVALIA_ANUAL = 0.1;
11
+ const CORPORATE_ISR_RETENCION = 0.1;
12
+ const CORPORATE_HORIZONTE_YEARS = 7;
13
+ const CORPORATE_OBRA_MESES = 39;
14
+ export const ENTRADA_BOOKING_AMOUNT_MXN = 10000;
15
+ export const APARTMENT_TOTAL_MONTHS = 36;
16
+ export const ENTRADA_ACTION_SCHEMES = [
17
+ {
18
+ id: "contado",
19
+ description: "Liquidas tu acción en una sola exhibición y accedes a tasa preferencial desde el mes 1.",
20
+ engancheRatio: 1,
21
+ label: "Contado",
22
+ monthlyCount: 0,
23
+ monthlyRatio: 0,
24
+ tag: "Máximo rendimiento",
25
+ tasaObra: 0.08,
26
+ tasaObraLabel: "8%",
27
+ },
28
+ {
29
+ id: "medio",
30
+ description: "Enganche de $120K y 5 mensualidades de $40K. Rendimiento del 6% anual una vez liquidado el capital.",
31
+ engancheRatio: 120000 / 320000,
32
+ label: "Financiado 5 meses",
33
+ monthlyCount: 5,
34
+ monthlyRatio: 40000 / 320000,
35
+ tag: "Balance",
36
+ tasaObra: 0.06,
37
+ tasaObraLabel: "6%",
38
+ },
39
+ {
40
+ id: "amplio",
41
+ description: "Enganche de $60K y 13 mensualidades de $20K. Rendimiento del 4% anual una vez liquidado el capital.",
42
+ engancheRatio: 60000 / 320000,
43
+ label: "Financiado 13 meses",
44
+ monthlyCount: 13,
45
+ monthlyRatio: 20000 / 320000,
46
+ tag: "Más accesible",
47
+ tasaObra: 0.04,
48
+ tasaObraLabel: "4%",
49
+ },
50
+ ];
51
+ function roundCurrency(value) {
52
+ return Math.round(value);
53
+ }
54
+ function clamp(value, min, max) {
55
+ return Math.min(max, Math.max(min, value));
56
+ }
57
+ function getActionScheme(schemeId) {
58
+ return (ENTRADA_ACTION_SCHEMES.find((scheme) => scheme.id === schemeId) ??
59
+ ENTRADA_ACTION_SCHEMES[0]);
60
+ }
61
+ export function getApartmentPartValueMXN(unit) {
62
+ return getUnitListPriceMXN(unit) / APARTMENT_TOTAL_PARTS;
63
+ }
64
+ export function buildApartmentPlan(unit, requestedParts, rentalRate) {
65
+ const numParts = clamp(requestedParts, 1, APARTMENT_TOTAL_PARTS);
66
+ const fullLotValueMXN = getUnitListPriceMXN(unit) * APARTMENT_COPROP_PRICE_MULTIPLIER;
67
+ const partValueMXN = fullLotValueMXN / APARTMENT_TOTAL_PARTS;
68
+ const totalRentMonthlyMXN = typeof rentalRate === "number"
69
+ ? (fullLotValueMXN * Math.max(rentalRate, 0)) / 12
70
+ : fullLotValueMXN * APARTMENT_TOTAL_RENT_RATIO;
71
+ const deliveryValueMXN = fullLotValueMXN * APARTMENT_DELIVERY_VALUE_RATIO;
72
+ const yourValueMXN = partValueMXN * numParts;
73
+ const engancheMXN = roundCurrency(yourValueMXN * APARTMENT_ENGANCHE_RATIO);
74
+ const monthlyPaymentMXN = roundCurrency(yourValueMXN * APARTMENT_MENSUALIDAD_RATIO);
75
+ const saldoFinalMXN = roundCurrency(yourValueMXN * APARTMENT_SALDO_FINAL_RATIO);
76
+ return {
77
+ deliveryValueMXN: roundCurrency((deliveryValueMXN / APARTMENT_TOTAL_PARTS) * numParts),
78
+ engancheMXN,
79
+ fullLotValueMXN: roundCurrency(fullLotValueMXN),
80
+ monthlyPaymentMXN,
81
+ numParts,
82
+ rentaMensualMXN: roundCurrency((totalRentMonthlyMXN / APARTMENT_TOTAL_PARTS) * numParts),
83
+ saldoFinalMXN,
84
+ totalMonths: APARTMENT_TOTAL_MONTHS,
85
+ totalToPayMXN: engancheMXN + monthlyPaymentMXN * APARTMENT_TOTAL_MONTHS + saldoFinalMXN,
86
+ yourValueMXN: roundCurrency(yourValueMXN),
87
+ };
88
+ }
89
+ export function getCorporatePricePerActionMXN(unit, totalShares) {
90
+ return getUnitListPriceMXN(unit) / Math.max(totalShares, 1);
91
+ }
92
+ export function getCorporateM2PerAction(unit, totalShares) {
93
+ return unit.areaM2 / Math.max(totalShares, 1);
94
+ }
95
+ export function buildCorporatePlan(args) {
96
+ const scheme = getActionScheme(args.requestedSchemeId);
97
+ const numActions = clamp(args.numActions, 1, 5);
98
+ const pricePerActionMXN = getCorporatePricePerActionMXN(args.unit, args.totalShares);
99
+ const m2PerAction = getCorporateM2PerAction(args.unit, args.totalShares);
100
+ const capitalMXN = pricePerActionMXN * numActions;
101
+ const engancheMXN = roundCurrency(capitalMXN * scheme.engancheRatio);
102
+ const monthlyPaymentMXN = roundCurrency(capitalMXN * scheme.monthlyRatio);
103
+ const monthsLiquidated = CORPORATE_OBRA_MESES - scheme.monthlyCount;
104
+ const obraMonthlyReturnMXN = (capitalMXN * scheme.tasaObra) / 12;
105
+ const obraMonthlyReturnNetMXN = obraMonthlyReturnMXN * (1 - CORPORATE_ISR_RETENCION);
106
+ const obraReturnNetMXN = obraMonthlyReturnNetMXN * monthsLiquidated;
107
+ const obraYearsFloat = CORPORATE_OBRA_MESES / 12;
108
+ const operationYears = CORPORATE_HORIZONTE_YEARS - obraYearsFloat;
109
+ const operationMonthlyReturnMXN = (capitalMXN * Math.max(args.rentalRate, 0)) / 12;
110
+ const operationMonthlyReturnNetMXN = operationMonthlyReturnMXN * (1 - CORPORATE_ISR_RETENCION);
111
+ const operationAnnualReturnNetMXN = operationMonthlyReturnNetMXN * 12;
112
+ const operationReturnHorizonNetMXN = getEscalatedAmountOverHorizonMXN(operationAnnualReturnNetMXN, args.inflationRate, operationYears);
113
+ const projectedAssetValueMXN = capitalMXN *
114
+ Math.pow(1 + CORPORATE_PLUSVALIA_ANUAL, CORPORATE_HORIZONTE_YEARS);
115
+ const plusvaliaGainMXN = projectedAssetValueMXN - capitalMXN;
116
+ const patrimonioTotalMXN = projectedAssetValueMXN + obraReturnNetMXN + operationReturnHorizonNetMXN;
117
+ return {
118
+ capitalMXN: roundCurrency(capitalMXN),
119
+ engancheMXN,
120
+ m2: m2PerAction * numActions,
121
+ monthlyCount: scheme.monthlyCount,
122
+ monthlyPaymentMXN,
123
+ numActions,
124
+ operationMonthlyReturnMXN: roundCurrency(operationMonthlyReturnMXN),
125
+ operationAnnualReturnNetMXN: roundCurrency(operationAnnualReturnNetMXN),
126
+ operationMonthlyReturnNetMXN: roundCurrency(operationMonthlyReturnNetMXN),
127
+ operationReturnHorizonNetMXN: roundCurrency(operationReturnHorizonNetMXN),
128
+ obraReturnMonthlyMXN: roundCurrency(obraMonthlyReturnMXN),
129
+ obraReturnMonthlyNetMXN: roundCurrency(obraMonthlyReturnNetMXN),
130
+ obraReturnNetMXN: roundCurrency(obraReturnNetMXN),
131
+ patrimonioTotalMXN: roundCurrency(patrimonioTotalMXN),
132
+ plusvaliaGainMXN: roundCurrency(plusvaliaGainMXN),
133
+ pricePerActionMXN: roundCurrency(pricePerActionMXN),
134
+ projectedAssetValueMXN: roundCurrency(projectedAssetValueMXN),
135
+ scheme,
136
+ totalMonths: scheme.monthlyCount,
137
+ totalPaidMXN: roundCurrency(capitalMXN),
138
+ };
139
+ }
@@ -0,0 +1,7 @@
1
+ export declare function formatCurrency(value: number): string;
2
+ export declare function formatCurrencyPrecise(value: number): string;
3
+ export declare function formatCompactCurrency(value: number): string;
4
+ export declare function formatPricePerM2(value: number): string;
5
+ export declare function formatArea(value: number): string;
6
+ export declare function formatPercent(value: number): string;
7
+ export declare function formatYears(value: number): string;
package/dist/format.js ADDED
@@ -0,0 +1,37 @@
1
+ const currencyFormatter = new Intl.NumberFormat("es-MX", {
2
+ style: "currency",
3
+ currency: "MXN",
4
+ maximumFractionDigits: 0,
5
+ });
6
+ const preciseCurrencyFormatter = new Intl.NumberFormat("es-MX", {
7
+ style: "currency",
8
+ currency: "MXN",
9
+ minimumFractionDigits: 2,
10
+ maximumFractionDigits: 2,
11
+ });
12
+ export function formatCurrency(value) {
13
+ return currencyFormatter.format(value);
14
+ }
15
+ export function formatCurrencyPrecise(value) {
16
+ return preciseCurrencyFormatter.format(value);
17
+ }
18
+ export function formatCompactCurrency(value) {
19
+ const absolute = Math.abs(value);
20
+ if (absolute >= 1000000) {
21
+ const sign = value < 0 ? "-" : "";
22
+ return `${sign}$${(absolute / 1000000).toFixed(2)}M`;
23
+ }
24
+ return formatCurrency(value);
25
+ }
26
+ export function formatPricePerM2(value) {
27
+ return `${formatCurrencyPrecise(value)}/m²`;
28
+ }
29
+ export function formatArea(value) {
30
+ return `${value.toFixed(2)} m²`;
31
+ }
32
+ export function formatPercent(value) {
33
+ return `${value}%`;
34
+ }
35
+ export function formatYears(value) {
36
+ return `${value} año${value === 1 ? "" : "s"}`;
37
+ }
package/dist/http.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { DesarrolloInventoryRequest, DesarrolloRequest, DesarrolloSitemapRequest, DesarrollosApiBaseOptions, DesarrollosApiErrorBody, CreatePublicReservationRequest, CreatePublicSessionRequest, GetPublicReservationRequest, PublicDevelopmentsResponse, PublicInventoryResponse, PublicInstancesResponse, PublicPaymentMethodsResponse, PublicReservationStatus, PublicSessionResponse, PublicSitemapResponse, PublicSnapshotResponse } from "./types.js";
2
+ type Credential = {
3
+ kind: "apiKey";
4
+ value: string;
5
+ } | {
6
+ kind: "session";
7
+ value: string;
8
+ };
9
+ export declare class DesarrollosApiError extends Error {
10
+ code?: string;
11
+ fieldErrors?: Record<string, string>;
12
+ response: Response;
13
+ status: number;
14
+ constructor(response: Response, body: DesarrollosApiErrorBody);
15
+ }
16
+ export declare function createDesarrollosApiRequester(options: DesarrollosApiBaseOptions): {
17
+ getAgentGuide: () => Promise<Record<string, unknown>>;
18
+ getOpenApi: () => Promise<Record<string, unknown>>;
19
+ listDevelopments: (credential: Credential) => Promise<PublicDevelopmentsResponse>;
20
+ listInstances: (args: Pick<DesarrolloRequest, "desarrolloKey">) => Promise<PublicInstancesResponse>;
21
+ getSnapshot: (args: DesarrolloRequest) => Promise<PublicSnapshotResponse>;
22
+ getInventory: (args: DesarrolloInventoryRequest) => Promise<PublicInventoryResponse>;
23
+ getSitemap: (args: DesarrolloSitemapRequest) => Promise<PublicSitemapResponse>;
24
+ getPaymentMethods: (args: DesarrolloRequest) => Promise<PublicPaymentMethodsResponse>;
25
+ createSession: (args: CreatePublicSessionRequest, credential: Credential) => Promise<PublicSessionResponse>;
26
+ createReservation: (args: CreatePublicReservationRequest, credential: Credential) => Promise<PublicReservationStatus>;
27
+ getReservation: (args: GetPublicReservationRequest, credential: Credential) => Promise<PublicReservationStatus>;
28
+ };
29
+ export {};
package/dist/http.js ADDED
@@ -0,0 +1,123 @@
1
+ export class DesarrollosApiError extends Error {
2
+ constructor(response, body) {
3
+ super(body.message);
4
+ this.name = "DesarrollosApiError";
5
+ this.code = body.code;
6
+ this.fieldErrors = body.fieldErrors;
7
+ this.response = response;
8
+ this.status = response.status;
9
+ }
10
+ }
11
+ function trimSlashes(value) {
12
+ return value.replace(/\/+$/, "");
13
+ }
14
+ function normalizeBaseUrl(baseUrl) {
15
+ const trimmed = trimSlashes(baseUrl.trim());
16
+ if (!trimmed) {
17
+ throw new Error("Desarrollos SDK baseUrl is required.");
18
+ }
19
+ return trimmed;
20
+ }
21
+ function getFetch(customFetch) {
22
+ const fetchImpl = customFetch ?? globalThis.fetch;
23
+ if (!fetchImpl) {
24
+ throw new Error("Desarrollos SDK requires a fetch implementation in this runtime.");
25
+ }
26
+ return fetchImpl;
27
+ }
28
+ function appendInstanceSlug(params, instanceSlug) {
29
+ if (instanceSlug) {
30
+ params.set("instanceSlug", instanceSlug);
31
+ }
32
+ }
33
+ function buildQuery(args) {
34
+ const params = new URLSearchParams();
35
+ appendInstanceSlug(params, args.instanceSlug);
36
+ if (args.unitIds && args.unitIds.length > 0) {
37
+ params.set("unitIds", args.unitIds.join(","));
38
+ }
39
+ if (args.includeShapes === false) {
40
+ params.set("includeShapes", "false");
41
+ }
42
+ const query = params.toString();
43
+ return query ? `?${query}` : "";
44
+ }
45
+ function encodePathSegment(value) {
46
+ return encodeURIComponent(value);
47
+ }
48
+ function credentialHeaders(credential) {
49
+ if (!credential) {
50
+ return [];
51
+ }
52
+ return credential.kind === "apiKey"
53
+ ? [["x-lapa-api-key", credential.value]]
54
+ : [["x-lapa-session", credential.value]];
55
+ }
56
+ function buildHeaders(headers, credential) {
57
+ const requestHeaders = new Headers(headers);
58
+ requestHeaders.set("Accept", requestHeaders.get("Accept") ?? "application/json");
59
+ for (const [name, value] of credentialHeaders(credential)) {
60
+ requestHeaders.set(name, value);
61
+ }
62
+ return requestHeaders;
63
+ }
64
+ async function readJsonResponse(response) {
65
+ const contentType = response.headers.get("content-type") ?? "";
66
+ const isJson = contentType.includes("application/json");
67
+ const body = isJson ? await response.json() : null;
68
+ if (!response.ok) {
69
+ throw new DesarrollosApiError(response, {
70
+ message: body && typeof body.message === "string"
71
+ ? body.message
72
+ : `Desarrollos API request failed with status ${response.status}.`,
73
+ code: body && typeof body.code === "string" ? body.code : undefined,
74
+ fieldErrors: body &&
75
+ typeof body.fieldErrors === "object" &&
76
+ body.fieldErrors !== null
77
+ ? body.fieldErrors
78
+ : undefined,
79
+ });
80
+ }
81
+ return body;
82
+ }
83
+ export function createDesarrollosApiRequester(options) {
84
+ const baseUrl = normalizeBaseUrl(options.baseUrl);
85
+ const fetchImpl = getFetch(options.fetch);
86
+ async function requestJson(path, init = {}) {
87
+ const { credential, headers, ...requestInit } = init;
88
+ const response = await fetchImpl(`${baseUrl}${path}`, {
89
+ ...requestInit,
90
+ headers: buildHeaders(headers, credential),
91
+ });
92
+ return await readJsonResponse(response);
93
+ }
94
+ return {
95
+ getAgentGuide: () => requestJson("/agent.json"),
96
+ getOpenApi: () => requestJson("/openapi.json"),
97
+ listDevelopments: (credential) => requestJson("/", { credential }),
98
+ listInstances: (args) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/instances`),
99
+ getSnapshot: (args) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/snapshot${buildQuery(args)}`),
100
+ getInventory: (args) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/inventory${buildQuery(args)}`),
101
+ getSitemap: (args) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/sitemap${buildQuery(args)}`),
102
+ getPaymentMethods: (args) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/payment-methods${buildQuery(args)}`),
103
+ createSession: (args, credential) => requestJson("/sessions", {
104
+ body: JSON.stringify({
105
+ desarrolloKey: args.desarrolloKey,
106
+ origin: args.origin,
107
+ }),
108
+ credential,
109
+ headers: { "Content-Type": "application/json" },
110
+ method: "POST",
111
+ }),
112
+ createReservation: (args, credential) => requestJson(`/${encodePathSegment(args.desarrolloKey)}/reservations`, {
113
+ body: JSON.stringify({
114
+ ...args.request,
115
+ instanceSlug: args.request.instanceSlug ?? args.instanceSlug,
116
+ }),
117
+ credential,
118
+ headers: { "Content-Type": "application/json" },
119
+ method: "POST",
120
+ }),
121
+ getReservation: (args, credential) => requestJson(`/reservations/${encodePathSegment(args.externalReference)}`, { credential }),
122
+ };
123
+ }
@@ -0,0 +1,15 @@
1
+ import { DesarrollosApiError } from "./http.js";
2
+ import type { DesarrollosApiBaseOptions, CreatePublicReservationRequest, GetPublicReservationRequest } from "./types.js";
3
+ export { DesarrollosApiError };
4
+ export type * from "./types.js";
5
+ export declare function createDesarrollosClient(options: DesarrollosApiBaseOptions): {
6
+ createReservation: (args: CreatePublicReservationRequest) => Promise<import("./types").PublicReservationStatus>;
7
+ getAgentGuide: () => Promise<Record<string, unknown>>;
8
+ getInventory: (args: import("./types").DesarrolloInventoryRequest) => Promise<import("./types").PublicInventoryResponse>;
9
+ getOpenApi: () => Promise<Record<string, unknown>>;
10
+ getPaymentMethods: (args: import("./types").DesarrolloRequest) => Promise<import("./types").PublicPaymentMethodsResponse>;
11
+ getReservation: (args: GetPublicReservationRequest) => Promise<import("./types").PublicReservationStatus>;
12
+ getSitemap: (args: import("./types").DesarrolloSitemapRequest) => Promise<import("./types").PublicSitemapResponse>;
13
+ getSnapshot: (args: import("./types").DesarrolloRequest) => Promise<import("./types").PublicSnapshotResponse>;
14
+ listInstances: (args: Pick<import("./types").DesarrolloRequest, "desarrolloKey">) => Promise<import("./types").PublicInstancesResponse>;
15
+ };
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import { DesarrollosApiError, createDesarrollosApiRequester } from "./http.js";
2
+ export { DesarrollosApiError };
3
+ export function createDesarrollosClient(options) {
4
+ const api = createDesarrollosApiRequester(options);
5
+ function requireSessionToken(sessionToken) {
6
+ if (!sessionToken) {
7
+ throw new Error("A desarrollos session token is required for browser write methods.");
8
+ }
9
+ return { kind: "session", value: sessionToken };
10
+ }
11
+ return {
12
+ createReservation: async (args) => api.createReservation(args, requireSessionToken(args.sessionToken)),
13
+ getAgentGuide: api.getAgentGuide,
14
+ getInventory: api.getInventory,
15
+ getOpenApi: api.getOpenApi,
16
+ getPaymentMethods: api.getPaymentMethods,
17
+ getReservation: async (args) => api.getReservation(args, requireSessionToken(args.sessionToken)),
18
+ getSitemap: api.getSitemap,
19
+ getSnapshot: api.getSnapshot,
20
+ listInstances: api.listInstances,
21
+ };
22
+ }
@@ -0,0 +1,30 @@
1
+ export declare const INSTALLMENT_DOWN_PAYMENT_RANGE: {
2
+ readonly min: 20;
3
+ readonly max: 100;
4
+ readonly step: 5;
5
+ };
6
+ export declare const INSTALLMENT_MONTHLY_BASE_RANGE: {
7
+ readonly min: 20;
8
+ readonly max: 40;
9
+ readonly step: 5;
10
+ };
11
+ export declare const INSTALLMENT_DEFAULT_DOWN_PAYMENT_PCT = 40;
12
+ export declare const INSTALLMENT_DEFAULT_MONTHLY_PCT = 20;
13
+ export declare const INSTALLMENT_DISCOUNT_THRESHOLD_PCT = 30;
14
+ export declare const INSTALLMENT_DISCOUNT_INCREMENT_PCT = 10;
15
+ export declare const INSTALLMENT_DISCOUNT_STEP_PCT = 1.25;
16
+ export declare function getInstallmentDiscountPct(downPaymentPct: number): number;
17
+ export declare function clampInstallmentDownPaymentPct(value: number): number;
18
+ export declare function getInstallmentMonthlyRange(downPaymentPct: number): {
19
+ min: number;
20
+ max: number;
21
+ step: 5;
22
+ };
23
+ export declare function clampInstallmentMonthlyPct(monthlyPct: number, downPaymentPct: number): number;
24
+ export interface InstallmentAllocation {
25
+ downPaymentPct: number;
26
+ monthlyPct: number;
27
+ deferredPct: number;
28
+ deliveryPct: number;
29
+ }
30
+ export declare function getInstallmentAllocation(downPaymentPct: number, monthlyPct: number): InstallmentAllocation;
@@ -0,0 +1,69 @@
1
+ export const INSTALLMENT_DOWN_PAYMENT_RANGE = {
2
+ min: 20,
3
+ max: 100,
4
+ step: 5,
5
+ };
6
+ export const INSTALLMENT_MONTHLY_BASE_RANGE = {
7
+ min: 20,
8
+ max: 40,
9
+ step: 5,
10
+ };
11
+ export const INSTALLMENT_DEFAULT_DOWN_PAYMENT_PCT = 40;
12
+ export const INSTALLMENT_DEFAULT_MONTHLY_PCT = 20;
13
+ export const INSTALLMENT_DISCOUNT_THRESHOLD_PCT = 30;
14
+ export const INSTALLMENT_DISCOUNT_INCREMENT_PCT = 10;
15
+ export const INSTALLMENT_DISCOUNT_STEP_PCT = 1.25;
16
+ export function getInstallmentDiscountPct(downPaymentPct) {
17
+ if (downPaymentPct < INSTALLMENT_DISCOUNT_THRESHOLD_PCT) {
18
+ return 0;
19
+ }
20
+ const steps = Math.floor((downPaymentPct - INSTALLMENT_DISCOUNT_THRESHOLD_PCT) /
21
+ INSTALLMENT_DISCOUNT_INCREMENT_PCT);
22
+ return (steps + 1) * INSTALLMENT_DISCOUNT_STEP_PCT;
23
+ }
24
+ const INSTALLMENT_BASE_DOWN_PAYMENT_PCT = INSTALLMENT_DOWN_PAYMENT_RANGE.min;
25
+ const INSTALLMENT_BASE_MONTHLY_PLUS_DEFERRED_PCT = INSTALLMENT_MONTHLY_BASE_RANGE.max;
26
+ function clampToRangeBounds(value, min, max, step) {
27
+ const clamped = Math.min(max, Math.max(min, value));
28
+ return min + Math.round((clamped - min) / step) * step;
29
+ }
30
+ export function clampInstallmentDownPaymentPct(value) {
31
+ return clampToRangeBounds(value, INSTALLMENT_DOWN_PAYMENT_RANGE.min, INSTALLMENT_DOWN_PAYMENT_RANGE.max, INSTALLMENT_DOWN_PAYMENT_RANGE.step);
32
+ }
33
+ export function getInstallmentMonthlyRange(downPaymentPct) {
34
+ const downPct = clampInstallmentDownPaymentPct(downPaymentPct);
35
+ const totalRemaining = 100 - downPct;
36
+ const max = Math.min(INSTALLMENT_MONTHLY_BASE_RANGE.max, totalRemaining);
37
+ const min = Math.min(INSTALLMENT_MONTHLY_BASE_RANGE.min, max);
38
+ return { min, max, step: INSTALLMENT_MONTHLY_BASE_RANGE.step };
39
+ }
40
+ export function clampInstallmentMonthlyPct(monthlyPct, downPaymentPct) {
41
+ const range = getInstallmentMonthlyRange(downPaymentPct);
42
+ return clampToRangeBounds(monthlyPct, range.min, range.max, range.step);
43
+ }
44
+ export function getInstallmentAllocation(downPaymentPct, monthlyPct) {
45
+ const downPct = clampInstallmentDownPaymentPct(downPaymentPct);
46
+ const baseMonthly = clampInstallmentMonthlyPct(monthlyPct, downPct);
47
+ const baseDeferred = INSTALLMENT_BASE_MONTHLY_PLUS_DEFERRED_PCT - baseMonthly;
48
+ const baseDelivery = 100 -
49
+ INSTALLMENT_BASE_DOWN_PAYMENT_PCT -
50
+ INSTALLMENT_BASE_MONTHLY_PLUS_DEFERRED_PCT;
51
+ let extra = Math.max(0, downPct - INSTALLMENT_BASE_DOWN_PAYMENT_PCT);
52
+ let delivery = baseDelivery;
53
+ let deferred = baseDeferred;
54
+ let monthly = baseMonthly;
55
+ const fromDeferred = Math.min(deferred, extra);
56
+ deferred -= fromDeferred;
57
+ extra -= fromDeferred;
58
+ const fromDelivery = Math.min(delivery, extra);
59
+ delivery -= fromDelivery;
60
+ extra -= fromDelivery;
61
+ const fromMonthly = Math.min(monthly, extra);
62
+ monthly -= fromMonthly;
63
+ return {
64
+ downPaymentPct: downPct,
65
+ monthlyPct: monthly,
66
+ deferredPct: deferred,
67
+ deliveryPct: delivery,
68
+ };
69
+ }
@@ -0,0 +1,10 @@
1
+ import type { AppContent, InventoryAvailability, InventoryAvailabilityByUnitId, InventoryStatus, InventoryStatusLabels, InventoryUnit } from "./types.js";
2
+ type PaidShareSalesByProductTypeId = Record<string, number>;
3
+ export declare function buildInventoryAvailabilityByUnitId(content: AppContent, paidShareSalesByProductTypeId?: PaidShareSalesByProductTypeId): InventoryAvailabilityByUnitId;
4
+ export declare function getInventoryAvailability(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId): InventoryAvailability | null;
5
+ export declare function getResolvedInventoryStatus(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId): InventoryStatus | null;
6
+ export declare function canCheckoutInventoryUnit(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId): boolean;
7
+ export declare function canCheckoutInventoryQuantity(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId, quantity: number): boolean;
8
+ export declare function getResolvedInventoryStatusLabel(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId, statusLabels: InventoryStatusLabels): string;
9
+ export declare function getResolvedShareSummary(unit: InventoryUnit | null | undefined, availabilityByUnitId: InventoryAvailabilityByUnitId): import("./types").InventoryShareAvailability | null;
10
+ export {};