@islamihab/kds 0.1.0 → 0.1.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.
Files changed (2) hide show
  1. package/index.js +261 -35
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -14404,8 +14404,16 @@ var printHelp = (help) => {
14404
14404
  description: `${field.description ?? ""}${isRequired(field) ? " (required)" : ""}`
14405
14405
  }));
14406
14406
  const usagePositionals = help.positionals?.map(([key, field]) => isRequired(field) ? ` <${key}>` : ` [${key}]`).join("") ?? "";
14407
- const options = help.options?.filter(({ field }) => !isHidden(field)).concat([{ key: "help", field: exports_external.boolean().default(false).describe("Show this help"), short: "h" }]).map(({ key, field, short }) => ({
14408
- label: `${short ? `-${short}, ` : " "}--${key}${valuePlaceholder(field)}`,
14407
+ const options = help.options?.filter(({ field }) => !isHidden(field)).concat([
14408
+ {
14409
+ key: "help",
14410
+ field: exports_external.boolean().default(false).describe("Show this help"),
14411
+ short: "h",
14412
+ negatedKey: undefined,
14413
+ negatedValue: undefined
14414
+ }
14415
+ ]).map(({ key, field, short, negatedKey }) => ({
14416
+ label: `${short ? `-${short}, ` : " "}--${key}${valuePlaceholder(field)}${negatedKey ? ` | --${negatedKey}` : ""}`,
14409
14417
  description: `${field.description ?? ""}${isRequired(field) ? " (required)" : ""}`
14410
14418
  }));
14411
14419
  const usageOptions = options?.length ? " [options]" : "";
@@ -14427,7 +14435,7 @@ ${title}:`);
14427
14435
  };
14428
14436
  var baseType = (field) => {
14429
14437
  let t = field;
14430
- while (t instanceof exports_external.ZodOptional || t instanceof exports_external.ZodDefault) {
14438
+ while (t instanceof exports_external.ZodOptional || t instanceof exports_external.ZodDefault || t instanceof exports_external.ZodNullable) {
14431
14439
  const inner = t.unwrap();
14432
14440
  if (!(inner instanceof exports_external.ZodType))
14433
14441
  return t;
@@ -14437,6 +14445,7 @@ var baseType = (field) => {
14437
14445
  };
14438
14446
  var isRequired = (field) => !(field instanceof exports_external.ZodOptional || field instanceof exports_external.ZodDefault);
14439
14447
  var isHidden = (field) => field.meta()?.hidden === true;
14448
+ var isNegatable = (field) => field.meta()?.negatable === true;
14440
14449
  var isHelpFlag = (arg) => arg === "-h" || arg === "--help";
14441
14450
  var valuePlaceholder = (field) => {
14442
14451
  const base = baseType(field);
@@ -14462,16 +14471,34 @@ var shortFlag = (key, field, usedShortFlags) => {
14462
14471
  usedShortFlags.add(short);
14463
14472
  return short;
14464
14473
  };
14474
+ var optionEntries = (shape) => {
14475
+ const usedShortFlags = new Set;
14476
+ const optionKeys = new Set(Object.keys(shape));
14477
+ return Object.entries(shape).map(([key, field]) => {
14478
+ const negatedKey = isNegatable(field) ? `no-${key}` : undefined;
14479
+ const negatedValue = negatedKey && baseType(field) instanceof exports_external.ZodBoolean ? false : negatedKey ? null : undefined;
14480
+ if (negatedKey && !field.safeParse(negatedValue).success) {
14481
+ throw new Error(`Option '--${key}' is negatable, but its schema does not accept ${String(negatedValue)}`);
14482
+ }
14483
+ if (negatedKey && optionKeys.has(negatedKey)) {
14484
+ throw new Error(`Option '--${negatedKey}' conflicts with the negated form of '--${key}'`);
14485
+ }
14486
+ return { key, field, short: shortFlag(key, field, usedShortFlags), negatedKey, negatedValue };
14487
+ });
14488
+ };
14465
14489
  var parseArgs = ({
14466
14490
  args,
14467
14491
  positionalEntries,
14468
14492
  optionsEntries,
14469
14493
  help
14470
14494
  }) => {
14471
- const options = Object.fromEntries(optionsEntries.map(({ key, field, short }) => {
14495
+ const options = {};
14496
+ for (const { key, field, short, negatedKey } of optionsEntries) {
14472
14497
  const type = baseType(field) instanceof exports_external.ZodBoolean ? "boolean" : "string";
14473
- return [key, short ? { short, type } : { type }];
14474
- }));
14498
+ options[key] = short ? { short, type } : { type };
14499
+ if (negatedKey)
14500
+ options[negatedKey] = { type: "boolean" };
14501
+ }
14475
14502
  try {
14476
14503
  const { values, positionals } = nodeParseArgs({
14477
14504
  args,
@@ -14482,7 +14509,18 @@ var parseArgs = ({
14482
14509
  help();
14483
14510
  throw new Error(`Unexpected argument '${positionals[positionalEntries.length]}'`);
14484
14511
  }
14485
- return { values, positionals };
14512
+ const normalizedValues = { ...values };
14513
+ for (const { key, negatedKey, negatedValue } of optionsEntries) {
14514
+ if (!negatedKey || !Object.hasOwn(normalizedValues, negatedKey))
14515
+ continue;
14516
+ if (Object.hasOwn(normalizedValues, key)) {
14517
+ help();
14518
+ throw new Error(`Options '--${key}' and '--${negatedKey}' cannot be used together`);
14519
+ }
14520
+ delete normalizedValues[negatedKey];
14521
+ normalizedValues[key] = negatedValue;
14522
+ }
14523
+ return { values: normalizedValues, positionals };
14486
14524
  } catch (error51) {
14487
14525
  if (error51 instanceof Error && "code" in error51) {
14488
14526
  if (error51.code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
@@ -14500,12 +14538,7 @@ var parseArgs = ({
14500
14538
  var command = (def) => {
14501
14539
  const inputShape = exports_external.object({ positionals: exports_external.object(def.positionals), options: exports_external.object(def.options) });
14502
14540
  const positionalEntries = Object.entries(inputShape.shape.positionals.shape);
14503
- const usedShortFlags = new Set;
14504
- const optionsEntries = Object.entries(inputShape.shape.options.shape).map(([key, field]) => ({
14505
- key,
14506
- field,
14507
- short: shortFlag(key, field, usedShortFlags)
14508
- }));
14541
+ const optionsEntries = optionEntries(inputShape.shape.options.shape);
14509
14542
  const run = async (args, path) => {
14510
14543
  const help = () => printHelp({ path, description: def.description, positionals: positionalEntries, options: optionsEntries });
14511
14544
  if (args.some(isHelpFlag))
@@ -14523,12 +14556,7 @@ var command = (def) => {
14523
14556
  };
14524
14557
  var group = (def) => {
14525
14558
  const optionsShape = exports_external.object(def.options);
14526
- const usedShortFlags = new Set;
14527
- const optionsEntries = Object.entries(optionsShape.shape).map(([key, field]) => ({
14528
- key,
14529
- field,
14530
- short: shortFlag(key, field, usedShortFlags)
14531
- }));
14559
+ const optionsEntries = optionEntries(optionsShape.shape);
14532
14560
  const run = async ([sub, ...rest], path) => {
14533
14561
  const help = () => printHelp({ path, description: def.description, commands: def.commands, options: optionsEntries });
14534
14562
  if (!sub && !def.run || sub === "help" || isHelpFlag(sub))
@@ -14557,7 +14585,7 @@ import { join } from "path";
14557
14585
  // package.json
14558
14586
  var package_default = {
14559
14587
  name: "cli",
14560
- version: "0.1.0",
14588
+ version: "0.1.2",
14561
14589
  private: true,
14562
14590
  type: "module",
14563
14591
  bin: {
@@ -15081,6 +15109,8 @@ var components = componentsGeneric();
15081
15109
 
15082
15110
  // ../../packages/backend/convex/constants.ts
15083
15111
  var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
15112
+ var PAGE_MODES = ["themed", "raw"];
15113
+ var PAGE_VISIBILITIES = ["public", "private"];
15084
15114
 
15085
15115
  // src/commands/auth/login.ts
15086
15116
  import open from "open";
@@ -17651,7 +17681,7 @@ var login = command({
17651
17681
  description: "Sign in securely through your browser",
17652
17682
  options: {
17653
17683
  server: exports_external.url().optional().describe("Override KDS server URL (only used in development)").meta({ hidden: true }),
17654
- "no-open": exports_external.boolean().optional().default(false).describe("Do not open the browser automatically")
17684
+ open: exports_external.boolean().optional().default(true).describe("Open the browser automatically").meta({ negatable: true })
17655
17685
  },
17656
17686
  run: async ({ options }) => {
17657
17687
  const oldConfig = await loadConfig({ throwIfMissing: false, throwIfInvalid: false });
@@ -17673,7 +17703,7 @@ Authorize this CLI in your browser:`);
17673
17703
  console.log(` ${browserUrl}`);
17674
17704
  console.log(`
17675
17705
  Device code: ${userCode}`);
17676
- if (!options["no-open"]) {
17706
+ if (options.open) {
17677
17707
  if (await openBrowser(browserUrl))
17678
17708
  console.log(`
17679
17709
  Opened your browser.`);
@@ -17723,25 +17753,221 @@ var auth = group({
17723
17753
  commands: [login, logout, status]
17724
17754
  });
17725
17755
 
17726
- // src/commands/hello.ts
17727
- var hello = command({
17728
- name: "hello",
17729
- description: "Example command \u2014 replace with a real tool",
17730
- hidden: true,
17756
+ // src/lib/group.ts
17757
+ import { basename } from "path";
17758
+ var git = async (...args) => {
17759
+ const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
17760
+ const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
17761
+ return exitCode === 0 ? output.trim() || null : null;
17762
+ };
17763
+ var repoNameFromRemote = (remote) => remote.replace(/\/+$/, "").replace(/\.git$/, "").split(/[/:]/).pop() || undefined;
17764
+ var detectRepoGroup = async () => {
17765
+ const remote = await git("remote", "get-url", "origin");
17766
+ if (remote)
17767
+ return repoNameFromRemote(remote);
17768
+ const root = await git("rev-parse", "--show-toplevel");
17769
+ return root ? basename(root) : undefined;
17770
+ };
17771
+ var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
17772
+
17773
+ // src/lib/input.ts
17774
+ var readBody = async (path) => path === "-" ? await Bun.stdin.text() : await Bun.file(path).text();
17775
+ var readPageId = (value) => {
17776
+ const trimmed = value.trim();
17777
+ try {
17778
+ const url2 = new URL(trimmed);
17779
+ const pagePath = url2.pathname.match(/\/p\/([^/]+)\/?$/);
17780
+ return pagePath?.[1] ?? trimmed;
17781
+ } catch {
17782
+ return trimmed;
17783
+ }
17784
+ };
17785
+
17786
+ // src/commands/pages/create.ts
17787
+ var create = command({
17788
+ name: "create",
17789
+ description: "Publish a page and print its URL",
17731
17790
  positionals: {
17732
- name: exports_external.string().default("world").describe("Who to greet")
17791
+ file: exports_external.string().describe("HTML file, or - for stdin")
17733
17792
  },
17734
17793
  options: {
17735
- shout: exports_external.boolean().default(false).describe("Greet loudly").meta({ short: "s" }),
17736
- times: exports_external.coerce.number().int().positive().default(1).describe("Number of times to greet").meta({ short: "t" })
17794
+ title: exports_external.string().describe("Page title").meta({ short: "t" }),
17795
+ group: exports_external.string().nullable().optional().describe("Group to list the page under (default: the current repo)").meta({ short: "g", negatable: true }),
17796
+ raw: exports_external.boolean().default(false).describe("Serve a full document verbatim (may run JS)"),
17797
+ private: exports_external.boolean().default(false).describe("Require the publishing account to view the page")
17737
17798
  },
17738
- run: ({ positionals: { name }, options: { shout, times } }) => {
17739
- const greeting = `Hello, ${name}!`;
17740
- for (let i2 = 0;i2 < times; i2++)
17741
- console.log(shout ? greeting.toUpperCase() : greeting);
17799
+ run: async ({ positionals: { file: file2 }, options: { title, group: group2, raw, private: isPrivate } }) => {
17800
+ const newGroup = await groupForCreate(group2);
17801
+ const html = await readBody(file2);
17802
+ const mode = raw ? "raw" : "themed";
17803
+ const visibility = isPrivate ? "private" : "public";
17804
+ const { url: url2 } = await (await backendClient()).action(api2.pages.create, {
17805
+ title,
17806
+ group: newGroup,
17807
+ html,
17808
+ mode,
17809
+ visibility
17810
+ });
17811
+ console.log(url2);
17742
17812
  }
17743
17813
  });
17744
17814
 
17815
+ // src/commands/pages/get.ts
17816
+ var get = command({
17817
+ name: "get",
17818
+ description: "Print a page's HTML",
17819
+ positionals: {
17820
+ id: exports_external.string().describe("Page id or URL")
17821
+ },
17822
+ options: {
17823
+ json: exports_external.boolean().default(false).describe("Print the full page as JSON"),
17824
+ version: exports_external.coerce.number().int().positive().optional().describe("Print a previous version instead of the current")
17825
+ },
17826
+ run: async ({ positionals: { id }, options: { json: json2, version: version4 } }) => {
17827
+ const page = await (await backendClient()).action(api2.pages.get, { id: readPageId(id), version: version4 });
17828
+ if (json2)
17829
+ console.log(JSON.stringify(page, null, 2));
17830
+ else
17831
+ process.stdout.write(page.html);
17832
+ }
17833
+ });
17834
+
17835
+ // ../../packages/backend/convex/lib/format.ts
17836
+ var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
17837
+
17838
+ // src/lib/output.ts
17839
+ var printTable = (rows) => {
17840
+ if (rows.length === 0)
17841
+ return;
17842
+ const columnCount = Math.max(...rows.map((row) => row.length));
17843
+ const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
17844
+ for (const row of rows) {
17845
+ console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
17846
+ }
17847
+ };
17848
+
17849
+ // src/commands/pages/list.ts
17850
+ var list = command({
17851
+ name: "list",
17852
+ description: "List your published pages",
17853
+ options: {
17854
+ group: exports_external.string().nullable().optional().describe("Only list pages in this group, or --no-group for the ungrouped ones").meta({ short: "g", negatable: true }),
17855
+ json: exports_external.boolean().default(false).describe("Print as JSON")
17856
+ },
17857
+ run: async ({ options: { group: group2, json: json2 } }) => {
17858
+ const pages = await (await backendClient()).query(api2.pages.list, { group: group2 });
17859
+ if (json2)
17860
+ return console.log(JSON.stringify(pages, null, 2));
17861
+ if (pages.length === 0)
17862
+ return console.log(group2 === undefined ? "No pages yet." : "No pages in that group.");
17863
+ printTable([
17864
+ ["TITLE", "GROUP", "ACCESS", "MODE", "SIZE", "VERSIONS", "CREATED", "URL"],
17865
+ ...pages.map((page) => [
17866
+ page.title,
17867
+ page.group ?? "-",
17868
+ page.visibility,
17869
+ page.mode,
17870
+ formatBytes(page.size),
17871
+ page.versions === 0 ? "-" : String(page.versions),
17872
+ new Date(page.createdAt).toISOString().slice(0, 10),
17873
+ page.url
17874
+ ])
17875
+ ]);
17876
+ }
17877
+ });
17878
+
17879
+ // src/commands/pages/remove.ts
17880
+ var remove = command({
17881
+ name: "delete",
17882
+ description: "Delete a page",
17883
+ positionals: {
17884
+ id: exports_external.string().describe("Page id or URL")
17885
+ },
17886
+ run: async ({ positionals: { id } }) => {
17887
+ await (await backendClient()).mutation(api2.pages.remove, { id: readPageId(id) });
17888
+ console.log("Deleted.");
17889
+ }
17890
+ });
17891
+
17892
+ // src/commands/pages/revert.ts
17893
+ var revert = command({
17894
+ name: "revert",
17895
+ description: "Restore a previous version as the page's content",
17896
+ positionals: {
17897
+ id: exports_external.string().describe("Page id or URL"),
17898
+ version: exports_external.coerce.number().int().positive().describe("Version number from `kds pages versions`")
17899
+ },
17900
+ run: async ({ positionals: { id, version: version4 } }) => {
17901
+ const { url: url2 } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
17902
+ console.log(url2);
17903
+ }
17904
+ });
17905
+
17906
+ // src/commands/pages/update.ts
17907
+ var update = command({
17908
+ name: "update",
17909
+ description: "Replace a page's HTML, title, group, mode, or visibility",
17910
+ positionals: {
17911
+ id: exports_external.string().describe("Page id or URL"),
17912
+ file: exports_external.string().optional().describe("HTML file, or - for stdin")
17913
+ },
17914
+ options: {
17915
+ title: exports_external.string().optional().describe("New page title").meta({ short: "t" }),
17916
+ group: exports_external.string().nullable().optional().describe("Move the page to this group, or --no-group to remove it from one").meta({ short: "g", negatable: true }),
17917
+ mode: exports_external.enum(PAGE_MODES).optional().describe("New serving mode"),
17918
+ visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page")
17919
+ },
17920
+ run: async ({ positionals: { id, file: file2 }, options: { title, group: group2, mode, visibility } }) => {
17921
+ if (!file2 && title === undefined && group2 === undefined && mode === undefined && visibility === undefined)
17922
+ throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --mode, or --visibility.");
17923
+ const html = file2 ? await readBody(file2) : undefined;
17924
+ const { url: url2 } = await (await backendClient()).action(api2.pages.update, {
17925
+ id: readPageId(id),
17926
+ html,
17927
+ title,
17928
+ group: group2,
17929
+ mode,
17930
+ visibility
17931
+ });
17932
+ console.log(url2);
17933
+ }
17934
+ });
17935
+
17936
+ // src/commands/pages/versions.ts
17937
+ var versions2 = command({
17938
+ name: "versions",
17939
+ description: "List a page's previous versions",
17940
+ positionals: {
17941
+ id: exports_external.string().describe("Page id or URL")
17942
+ },
17943
+ options: {
17944
+ json: exports_external.boolean().default(false).describe("Print as JSON")
17945
+ },
17946
+ run: async ({ positionals: { id }, options: { json: json2 } }) => {
17947
+ const archived = await (await backendClient()).query(api2.pages.versions, { id: readPageId(id) });
17948
+ if (json2)
17949
+ return console.log(JSON.stringify(archived, null, 2));
17950
+ if (archived.length === 0)
17951
+ return console.log("No previous versions.");
17952
+ printTable([
17953
+ ["VERSION", "MODE", "SIZE", "ARCHIVED"],
17954
+ ...archived.map((version4) => [
17955
+ String(version4.version),
17956
+ version4.mode,
17957
+ formatBytes(version4.size),
17958
+ new Date(version4.archivedAt).toISOString().slice(0, 10)
17959
+ ])
17960
+ ]);
17961
+ }
17962
+ });
17963
+
17964
+ // src/commands/pages/index.ts
17965
+ var pages = group({
17966
+ name: "pages",
17967
+ description: "Publish HTML documents to the web",
17968
+ commands: [create, list, get, update, versions2, revert, remove]
17969
+ });
17970
+
17745
17971
  // src/commands/upgrade.ts
17746
17972
  var upgrade = command({
17747
17973
  name: "upgrade",
@@ -17757,7 +17983,7 @@ var upgrade = command({
17757
17983
  var rootCommand = group({
17758
17984
  name: "kds",
17759
17985
  description: "KDS CLI",
17760
- commands: [auth, hello, upgrade],
17986
+ commands: [auth, pages, upgrade],
17761
17987
  options: {
17762
17988
  version: exports_external.boolean().default(false).describe("Show the version").meta({ short: "v" })
17763
17989
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {