@tostudy-ai/cli 0.13.0 → 0.14.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.
- package/bin/tostudy.ts +2 -0
- package/dist/cli.js +143 -34
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/bin/tostudy.ts
CHANGED
|
@@ -5,8 +5,10 @@ if (!process.env.LOG_LEVEL) {
|
|
|
5
5
|
|
|
6
6
|
import { createProgram, CLI_VERSION } from "../src/cli.js";
|
|
7
7
|
import { checkForUpdates } from "../src/update-checker.js";
|
|
8
|
+
import { maybeAutoUpdate } from "../src/auto-updater.js";
|
|
8
9
|
|
|
9
10
|
const program = createProgram();
|
|
10
11
|
program.parse();
|
|
11
12
|
|
|
12
13
|
checkForUpdates(CLI_VERSION);
|
|
14
|
+
maybeAutoUpdate(CLI_VERSION);
|
package/dist/cli.js
CHANGED
|
@@ -2804,7 +2804,7 @@ var CLI_VERSION;
|
|
|
2804
2804
|
var init_version = __esm({
|
|
2805
2805
|
"src/version.ts"() {
|
|
2806
2806
|
"use strict";
|
|
2807
|
-
CLI_VERSION = true ? "0.
|
|
2807
|
+
CLI_VERSION = true ? "0.14.0" : "0.7.1";
|
|
2808
2808
|
}
|
|
2809
2809
|
});
|
|
2810
2810
|
|
|
@@ -2814,22 +2814,24 @@ __export(update_checker_exports, {
|
|
|
2814
2814
|
checkForUpdates: () => checkForUpdates,
|
|
2815
2815
|
checkVersionSync: () => checkVersionSync,
|
|
2816
2816
|
fetchLatestVersion: () => fetchLatestVersion,
|
|
2817
|
-
isNewerVersion: () => isNewerVersion
|
|
2817
|
+
isNewerVersion: () => isNewerVersion,
|
|
2818
|
+
readCache: () => readCache,
|
|
2819
|
+
writeCache: () => writeCache
|
|
2818
2820
|
});
|
|
2819
2821
|
import fs7 from "node:fs";
|
|
2820
2822
|
import path9 from "node:path";
|
|
2821
|
-
function readCache() {
|
|
2823
|
+
function readCache(configDir) {
|
|
2822
2824
|
try {
|
|
2823
|
-
const p = path9.join(getConfigDir(), CACHE_FILE);
|
|
2825
|
+
const p = path9.join(configDir ?? getConfigDir(), CACHE_FILE);
|
|
2824
2826
|
if (!fs7.existsSync(p)) return null;
|
|
2825
2827
|
return JSON.parse(fs7.readFileSync(p, "utf-8"));
|
|
2826
2828
|
} catch {
|
|
2827
2829
|
return null;
|
|
2828
2830
|
}
|
|
2829
2831
|
}
|
|
2830
|
-
function writeCache(cache) {
|
|
2832
|
+
function writeCache(cache, configDir) {
|
|
2831
2833
|
try {
|
|
2832
|
-
const dir = getConfigDir();
|
|
2834
|
+
const dir = configDir ?? getConfigDir();
|
|
2833
2835
|
fs7.mkdirSync(dir, { recursive: true });
|
|
2834
2836
|
fs7.writeFileSync(path9.join(dir, CACHE_FILE), JSON.stringify(cache));
|
|
2835
2837
|
} catch {
|
|
@@ -5468,7 +5470,7 @@ var init_query_promise = __esm({
|
|
|
5468
5470
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
5469
5471
|
const nullifyMap = {};
|
|
5470
5472
|
const result = columns.reduce(
|
|
5471
|
-
(result2, { path:
|
|
5473
|
+
(result2, { path: path24, field }, columnIndex) => {
|
|
5472
5474
|
let decoder;
|
|
5473
5475
|
if (is(field, Column)) {
|
|
5474
5476
|
decoder = field;
|
|
@@ -5480,8 +5482,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
5480
5482
|
decoder = field.sql.decoder;
|
|
5481
5483
|
}
|
|
5482
5484
|
let node = result2;
|
|
5483
|
-
for (const [pathChunkIndex, pathChunk] of
|
|
5484
|
-
if (pathChunkIndex <
|
|
5485
|
+
for (const [pathChunkIndex, pathChunk] of path24.entries()) {
|
|
5486
|
+
if (pathChunkIndex < path24.length - 1) {
|
|
5485
5487
|
if (!(pathChunk in node)) {
|
|
5486
5488
|
node[pathChunk] = {};
|
|
5487
5489
|
}
|
|
@@ -5489,8 +5491,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
5489
5491
|
} else {
|
|
5490
5492
|
const rawValue = row[columnIndex];
|
|
5491
5493
|
const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
|
|
5492
|
-
if (joinsNotNullableMap && is(field, Column) &&
|
|
5493
|
-
const objectName =
|
|
5494
|
+
if (joinsNotNullableMap && is(field, Column) && path24.length === 2) {
|
|
5495
|
+
const objectName = path24[0];
|
|
5494
5496
|
if (!(objectName in nullifyMap)) {
|
|
5495
5497
|
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
5496
5498
|
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
@@ -9437,13 +9439,13 @@ function Subscribe(postgres2, options) {
|
|
|
9437
9439
|
}
|
|
9438
9440
|
}
|
|
9439
9441
|
function handle(a, b2) {
|
|
9440
|
-
const
|
|
9442
|
+
const path24 = b2.relation.schema + "." + b2.relation.table;
|
|
9441
9443
|
call("*", a, b2);
|
|
9442
|
-
call("*:" +
|
|
9443
|
-
b2.relation.keys.length && call("*:" +
|
|
9444
|
+
call("*:" + path24, a, b2);
|
|
9445
|
+
b2.relation.keys.length && call("*:" + path24 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
|
|
9444
9446
|
call(b2.command, a, b2);
|
|
9445
|
-
call(b2.command + ":" +
|
|
9446
|
-
b2.relation.keys.length && call(b2.command + ":" +
|
|
9447
|
+
call(b2.command + ":" + path24, a, b2);
|
|
9448
|
+
b2.relation.keys.length && call(b2.command + ":" + path24 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
|
|
9447
9449
|
}
|
|
9448
9450
|
function pong() {
|
|
9449
9451
|
const x2 = Buffer.alloc(34);
|
|
@@ -9556,8 +9558,8 @@ function parseEvent(x) {
|
|
|
9556
9558
|
const xs = x.match(/^(\*|insert|update|delete)?:?([^.]+?\.?[^=]+)?=?(.+)?/i) || [];
|
|
9557
9559
|
if (!xs)
|
|
9558
9560
|
throw new Error("Malformed subscribe pattern: " + x);
|
|
9559
|
-
const [, command,
|
|
9560
|
-
return (command || "*") + (
|
|
9561
|
+
const [, command, path24, key] = xs;
|
|
9562
|
+
return (command || "*") + (path24 ? ":" + (path24.indexOf(".") === -1 ? "public." + path24 : path24) : "") + (key ? "=" + key : "");
|
|
9561
9563
|
}
|
|
9562
9564
|
var noop2;
|
|
9563
9565
|
var init_subscribe = __esm({
|
|
@@ -9695,10 +9697,10 @@ function Postgres(a, b2) {
|
|
|
9695
9697
|
});
|
|
9696
9698
|
return query;
|
|
9697
9699
|
}
|
|
9698
|
-
function file2(
|
|
9700
|
+
function file2(path24, args = [], options2 = {}) {
|
|
9699
9701
|
arguments.length === 2 && !Array.isArray(args) && (options2 = args, args = []);
|
|
9700
9702
|
const query = new Query([], args, (query2) => {
|
|
9701
|
-
fs11.readFile(
|
|
9703
|
+
fs11.readFile(path24, "utf8", (err, string4) => {
|
|
9702
9704
|
if (err)
|
|
9703
9705
|
return query2.reject(err);
|
|
9704
9706
|
query2.strings = [string4];
|
|
@@ -14756,10 +14758,10 @@ function mergeDefs(...defs) {
|
|
|
14756
14758
|
function cloneDef(schema) {
|
|
14757
14759
|
return mergeDefs(schema._zod.def);
|
|
14758
14760
|
}
|
|
14759
|
-
function getElementAtPath(obj,
|
|
14760
|
-
if (!
|
|
14761
|
+
function getElementAtPath(obj, path24) {
|
|
14762
|
+
if (!path24)
|
|
14761
14763
|
return obj;
|
|
14762
|
-
return
|
|
14764
|
+
return path24.reduce((acc, key) => acc?.[key], obj);
|
|
14763
14765
|
}
|
|
14764
14766
|
function promiseAllObject(promisesObj) {
|
|
14765
14767
|
const keys = Object.keys(promisesObj);
|
|
@@ -15071,11 +15073,11 @@ function aborted(x, startIndex = 0) {
|
|
|
15071
15073
|
}
|
|
15072
15074
|
return false;
|
|
15073
15075
|
}
|
|
15074
|
-
function prefixIssues(
|
|
15076
|
+
function prefixIssues(path24, issues) {
|
|
15075
15077
|
return issues.map((iss) => {
|
|
15076
15078
|
var _a2;
|
|
15077
15079
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
15078
|
-
iss.path.unshift(
|
|
15080
|
+
iss.path.unshift(path24);
|
|
15079
15081
|
return iss;
|
|
15080
15082
|
});
|
|
15081
15083
|
}
|
|
@@ -15317,7 +15319,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
|
|
|
15317
15319
|
}
|
|
15318
15320
|
function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
15319
15321
|
const result = { errors: [] };
|
|
15320
|
-
const processError = (error50,
|
|
15322
|
+
const processError = (error50, path24 = []) => {
|
|
15321
15323
|
var _a2, _b;
|
|
15322
15324
|
for (const issue2 of error50.issues) {
|
|
15323
15325
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -15327,7 +15329,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
15327
15329
|
} else if (issue2.code === "invalid_element") {
|
|
15328
15330
|
processError({ issues: issue2.issues }, issue2.path);
|
|
15329
15331
|
} else {
|
|
15330
|
-
const fullpath = [...
|
|
15332
|
+
const fullpath = [...path24, ...issue2.path];
|
|
15331
15333
|
if (fullpath.length === 0) {
|
|
15332
15334
|
result.errors.push(mapper(issue2));
|
|
15333
15335
|
continue;
|
|
@@ -15359,8 +15361,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
15359
15361
|
}
|
|
15360
15362
|
function toDotPath(_path) {
|
|
15361
15363
|
const segs = [];
|
|
15362
|
-
const
|
|
15363
|
-
for (const seg of
|
|
15364
|
+
const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
15365
|
+
for (const seg of path24) {
|
|
15364
15366
|
if (typeof seg === "number")
|
|
15365
15367
|
segs.push(`[${seg}]`);
|
|
15366
15368
|
else if (typeof seg === "symbol")
|
|
@@ -28054,13 +28056,13 @@ function resolveRef(ref, ctx) {
|
|
|
28054
28056
|
if (!ref.startsWith("#")) {
|
|
28055
28057
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
28056
28058
|
}
|
|
28057
|
-
const
|
|
28058
|
-
if (
|
|
28059
|
+
const path24 = ref.slice(1).split("/").filter(Boolean);
|
|
28060
|
+
if (path24.length === 0) {
|
|
28059
28061
|
return ctx.rootSchema;
|
|
28060
28062
|
}
|
|
28061
28063
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
28062
|
-
if (
|
|
28063
|
-
const key =
|
|
28064
|
+
if (path24[0] === defsKey) {
|
|
28065
|
+
const key = path24[1];
|
|
28064
28066
|
if (!key || !ctx.defs[key]) {
|
|
28065
28067
|
throw new Error(`Reference not found: ${ref}`);
|
|
28066
28068
|
}
|
|
@@ -30323,6 +30325,10 @@ var init_credit_packages = __esm({
|
|
|
30323
30325
|
}).notNull(),
|
|
30324
30326
|
isActive: boolean("is_active").notNull().default(true),
|
|
30325
30327
|
includesCatalog: boolean("includes_catalog").notNull().default(true),
|
|
30328
|
+
// Subscription tiers 2026-07-07 spec: % off AI-usage debits (0 = none).
|
|
30329
|
+
consumptionDiscountPercent: integer("consumption_discount_percent").notNull().default(0),
|
|
30330
|
+
// Course enrollments redeemable per billing cycle (0 = none; Pro = 1).
|
|
30331
|
+
courseEnrollmentsPerMonth: integer("course_enrollments_per_month").notNull().default(0),
|
|
30326
30332
|
sortOrder: integer("sort_order").notNull().default(0),
|
|
30327
30333
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
|
|
30328
30334
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
|
|
@@ -30330,7 +30336,12 @@ var init_credit_packages = __esm({
|
|
|
30330
30336
|
(table) => ({
|
|
30331
30337
|
creditsPositive: check("csp_credits_positive", sql`${table.creditsPerMonth} > 0`),
|
|
30332
30338
|
pricePositive: check("csp_price_positive", sql`${table.priceUsdMonthly} > 0`),
|
|
30333
|
-
nameSlugPattern: check("csp_name_slug_pattern", sql`${table.name} ~ '^[a-z][a-z0-9_]{0,49}$'`)
|
|
30339
|
+
nameSlugPattern: check("csp_name_slug_pattern", sql`${table.name} ~ '^[a-z][a-z0-9_]{0,49}$'`),
|
|
30340
|
+
discountRange: check(
|
|
30341
|
+
"csp_discount_range",
|
|
30342
|
+
sql`${table.consumptionDiscountPercent} >= 0 AND ${table.consumptionDiscountPercent} <= 90`
|
|
30343
|
+
),
|
|
30344
|
+
quotaNonNegative: check("csp_quota_non_negative", sql`${table.courseEnrollmentsPerMonth} >= 0`)
|
|
30334
30345
|
})
|
|
30335
30346
|
);
|
|
30336
30347
|
creditSubscriptions = pgTable(
|
|
@@ -43206,6 +43217,38 @@ var init_email_delivery = __esm({
|
|
|
43206
43217
|
}
|
|
43207
43218
|
});
|
|
43208
43219
|
|
|
43220
|
+
// ../../packages/database/src/schema/subscription-redemptions.ts
|
|
43221
|
+
var subscriptionCourseRedemptions;
|
|
43222
|
+
var init_subscription_redemptions = __esm({
|
|
43223
|
+
"../../packages/database/src/schema/subscription-redemptions.ts"() {
|
|
43224
|
+
"use strict";
|
|
43225
|
+
init_pg_core();
|
|
43226
|
+
init_users();
|
|
43227
|
+
init_courses3();
|
|
43228
|
+
init_enrollments();
|
|
43229
|
+
init_credit_packages();
|
|
43230
|
+
subscriptionCourseRedemptions = pgTable(
|
|
43231
|
+
"subscription_course_redemptions",
|
|
43232
|
+
{
|
|
43233
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
43234
|
+
subscriptionId: uuid("subscription_id").references(() => creditSubscriptions.id, { onDelete: "restrict" }).notNull(),
|
|
43235
|
+
userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
|
|
43236
|
+
courseId: uuid("course_id").references(() => courses.id, { onDelete: "restrict" }).notNull(),
|
|
43237
|
+
enrollmentId: uuid("enrollment_id").references(() => enrollments.id, { onDelete: "restrict" }).notNull(),
|
|
43238
|
+
cycleStart: timestamp("cycle_start", { withTimezone: true }).notNull(),
|
|
43239
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
43240
|
+
},
|
|
43241
|
+
(table) => ({
|
|
43242
|
+
oncePerCycle: uniqueIndex("scr_subscription_cycle_uq").on(
|
|
43243
|
+
table.subscriptionId,
|
|
43244
|
+
table.cycleStart
|
|
43245
|
+
),
|
|
43246
|
+
userIdx: index("scr_user_idx").on(table.userId)
|
|
43247
|
+
})
|
|
43248
|
+
);
|
|
43249
|
+
}
|
|
43250
|
+
});
|
|
43251
|
+
|
|
43209
43252
|
// ../../packages/database/src/schema/index.ts
|
|
43210
43253
|
var schema_exports = {};
|
|
43211
43254
|
__export(schema_exports, {
|
|
@@ -43628,6 +43671,7 @@ __export(schema_exports, {
|
|
|
43628
43671
|
studyModuleSessionsRelations: () => studyModuleSessionsRelations,
|
|
43629
43672
|
studyPhaseV2Values: () => studyPhaseV2Values,
|
|
43630
43673
|
studyPhaseValues: () => studyPhaseValues,
|
|
43674
|
+
subscriptionCourseRedemptions: () => subscriptionCourseRedemptions,
|
|
43631
43675
|
subscriptionPoolStatusEnum: () => subscriptionPoolStatusEnum,
|
|
43632
43676
|
subscriptionSplitStatusEnum: () => subscriptionSplitStatusEnum,
|
|
43633
43677
|
supportedCurrencies: () => supportedCurrencies,
|
|
@@ -43801,6 +43845,7 @@ var init_schema3 = __esm({
|
|
|
43801
43845
|
init_user_profiles();
|
|
43802
43846
|
init_user_resource_audits();
|
|
43803
43847
|
init_email_delivery();
|
|
43848
|
+
init_subscription_redemptions();
|
|
43804
43849
|
}
|
|
43805
43850
|
});
|
|
43806
43851
|
|
|
@@ -47937,13 +47982,77 @@ var init_cli = __esm({
|
|
|
47937
47982
|
}
|
|
47938
47983
|
});
|
|
47939
47984
|
|
|
47985
|
+
// src/auto-updater.ts
|
|
47986
|
+
var auto_updater_exports = {};
|
|
47987
|
+
__export(auto_updater_exports, {
|
|
47988
|
+
detectManager: () => detectManager,
|
|
47989
|
+
maybeAutoUpdate: () => maybeAutoUpdate
|
|
47990
|
+
});
|
|
47991
|
+
import fs21 from "node:fs";
|
|
47992
|
+
import path23 from "node:path";
|
|
47993
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
47994
|
+
function detectManager(entryRealPath) {
|
|
47995
|
+
const p = entryRealPath.split(path23.sep).join("/");
|
|
47996
|
+
if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
|
|
47997
|
+
if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
|
|
47998
|
+
if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
|
|
47999
|
+
return { cmd: "npm", args: ["i", "-g", `${PACKAGE_NAME2}@latest`] };
|
|
48000
|
+
return null;
|
|
48001
|
+
}
|
|
48002
|
+
function installDir(entryRealPath) {
|
|
48003
|
+
const normalized = entryRealPath.split(path23.sep).join("/");
|
|
48004
|
+
const marker = `/node_modules/${PACKAGE_NAME2}/`;
|
|
48005
|
+
const idx = normalized.indexOf(marker);
|
|
48006
|
+
if (idx === -1) return path23.dirname(entryRealPath);
|
|
48007
|
+
return entryRealPath.slice(0, idx + marker.length - 1);
|
|
48008
|
+
}
|
|
48009
|
+
function maybeAutoUpdate(currentVersion, opts = {}) {
|
|
48010
|
+
try {
|
|
48011
|
+
const env = opts.env ?? process.env;
|
|
48012
|
+
const argv = opts.argv ?? process.argv;
|
|
48013
|
+
const isTTY = opts.isTTY ?? process.stderr.isTTY === true;
|
|
48014
|
+
if (env["TOSTUDY_NO_AUTO_UPDATE"]) return;
|
|
48015
|
+
if (env["CI"] || !isTTY || argv.includes("--json")) return;
|
|
48016
|
+
const configDir = opts.configDir ?? getConfigDir();
|
|
48017
|
+
const cache = readCache(configDir);
|
|
48018
|
+
if (!cache || !isNewerVersion(cache.latest, currentVersion)) return;
|
|
48019
|
+
if (cache.attemptedVersion === cache.latest) return;
|
|
48020
|
+
const entryPath = opts.entryPath ?? argv[1];
|
|
48021
|
+
if (!entryPath) return;
|
|
48022
|
+
const real2 = fs21.realpathSync(entryPath);
|
|
48023
|
+
const mgr = detectManager(real2);
|
|
48024
|
+
if (!mgr) return;
|
|
48025
|
+
fs21.accessSync(installDir(real2), fs21.constants.W_OK);
|
|
48026
|
+
writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
|
|
48027
|
+
process.stderr.write(
|
|
48028
|
+
`
|
|
48029
|
+
Atualizando ${PACKAGE_NAME2} para ${cache.latest} em segundo plano\u2026
|
|
48030
|
+
`
|
|
48031
|
+
);
|
|
48032
|
+
const spawnFn = opts.spawnFn ?? spawn2;
|
|
48033
|
+
spawnFn(mgr.cmd, mgr.args, { detached: true, stdio: "ignore" }).unref();
|
|
48034
|
+
} catch {
|
|
48035
|
+
}
|
|
48036
|
+
}
|
|
48037
|
+
var PACKAGE_NAME2;
|
|
48038
|
+
var init_auto_updater = __esm({
|
|
48039
|
+
"src/auto-updater.ts"() {
|
|
48040
|
+
"use strict";
|
|
48041
|
+
init_config_dir();
|
|
48042
|
+
init_update_checker();
|
|
48043
|
+
PACKAGE_NAME2 = "@tostudy-ai/cli";
|
|
48044
|
+
}
|
|
48045
|
+
});
|
|
48046
|
+
|
|
47940
48047
|
// src/cli-entry.ts
|
|
47941
48048
|
if (!process.env.LOG_LEVEL) {
|
|
47942
48049
|
process.env.LOG_LEVEL = "fatal";
|
|
47943
48050
|
}
|
|
47944
48051
|
var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
|
|
47945
48052
|
var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
|
|
48053
|
+
var { maybeAutoUpdate: maybeAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_updater(), auto_updater_exports));
|
|
47946
48054
|
var program = createProgram2();
|
|
47947
48055
|
program.parse();
|
|
47948
48056
|
checkForUpdates2(CLI_VERSION2);
|
|
48057
|
+
maybeAutoUpdate2(CLI_VERSION2);
|
|
47949
48058
|
//# sourceMappingURL=cli.js.map
|