tthr 0.3.1 → 0.3.3

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
@@ -11,10 +11,13 @@ import {
11
11
  requireAuth,
12
12
  resolvePath,
13
13
  saveCredentials
14
- } from "./chunk-HNRJ3DYB.js";
14
+ } from "./chunk-QUS263RB.js";
15
15
 
16
16
  // src/index.ts
17
17
  import { Command } from "commander";
18
+ import fs8 from "fs-extra";
19
+ import { fileURLToPath } from "url";
20
+ import path8 from "path";
18
21
 
19
22
  // src/commands/init.ts
20
23
  import chalk2 from "chalk";
@@ -111,6 +114,12 @@ async function deploySchemaToServer(projectId, token, schemaPath, environment, d
111
114
  const schemaUrl = `${API_URL2}/projects/${projectId}/env/${environment}/deploy/schema`;
112
115
  console.log(chalk.dim(`
113
116
  URL: ${schemaUrl}`));
117
+ const rlsConfigs = tables.filter((t) => t.rls).map((t) => ({
118
+ tableName: t.name,
119
+ enabled: t.rls.enabled,
120
+ defaultDeny: t.rls.defaultDeny ?? true,
121
+ policies: t.rls.policies
122
+ }));
114
123
  const requestBody = {
115
124
  sql,
116
125
  tables: tables.map((t) => ({
@@ -119,6 +128,9 @@ async function deploySchemaToServer(projectId, token, schemaPath, environment, d
119
128
  columns: t.columns
120
129
  }))
121
130
  };
131
+ if (rlsConfigs.length > 0) {
132
+ requestBody.rls = rlsConfigs;
133
+ }
122
134
  const response = await fetch(schemaUrl, {
123
135
  method: "POST",
124
136
  headers: {
@@ -277,10 +289,11 @@ function parseSchema(source) {
277
289
  if (!schemaMatch) return tables;
278
290
  const schemaContent = schemaMatch[1];
279
291
  const schemaStartIndex = source.indexOf(schemaMatch[0]) + "defineSchema({".length;
280
- const tableStartRegex = /(\w+)\s*:\s*\{/g;
292
+ const tableStartRegex = /(\w+)\s*:\s*(?:defineTable\s*\(\s*)?\{/g;
281
293
  let match;
282
294
  while ((match = tableStartRegex.exec(schemaContent)) !== null) {
283
295
  const tableName = match[1];
296
+ const isDefineTable = match[0].includes("defineTable");
284
297
  const startOffset = match.index + match[0].length;
285
298
  let braceCount = 1;
286
299
  let endOffset = startOffset;
@@ -290,7 +303,28 @@ function parseSchema(source) {
290
303
  else if (char === "}") braceCount--;
291
304
  endOffset = i;
292
305
  }
293
- const columnsContent = schemaContent.slice(startOffset, endOffset);
306
+ let rawContent = schemaContent.slice(startOffset, endOffset);
307
+ let rlsConfig;
308
+ let columnsContent;
309
+ if (isDefineTable) {
310
+ const columnsBlockMatch = rawContent.match(/columns\s*:\s*\{/);
311
+ if (columnsBlockMatch) {
312
+ const colStart = rawContent.indexOf(columnsBlockMatch[0]) + columnsBlockMatch[0].length;
313
+ let colBraceCount = 1;
314
+ let colEnd = colStart;
315
+ for (let i = colStart; i < rawContent.length && colBraceCount > 0; i++) {
316
+ if (rawContent[i] === "{") colBraceCount++;
317
+ else if (rawContent[i] === "}") colBraceCount--;
318
+ colEnd = i;
319
+ }
320
+ columnsContent = rawContent.slice(colStart, colEnd);
321
+ } else {
322
+ columnsContent = rawContent;
323
+ }
324
+ rlsConfig = parseRlsConfig(rawContent);
325
+ } else {
326
+ columnsContent = rawContent;
327
+ }
294
328
  const lines = columnsContent.split("\n");
295
329
  const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
296
330
  const minIndent = nonEmptyLines.reduce((min, line) => {
@@ -334,10 +368,74 @@ function parseSchema(source) {
334
368
  oneOf
335
369
  };
336
370
  }
337
- tables.push({ name: tableName, columns, source: tableSource });
371
+ tables.push({ name: tableName, columns, source: tableSource, rls: rlsConfig });
338
372
  }
339
373
  return tables;
340
374
  }
375
+ function parseRlsConfig(content) {
376
+ const rlsMatch = content.match(/rls\s*:\s*\{/);
377
+ if (!rlsMatch) return void 0;
378
+ const rlsStart = content.indexOf(rlsMatch[0]) + rlsMatch[0].length;
379
+ let braceCount = 1;
380
+ let rlsEnd = rlsStart;
381
+ for (let i = rlsStart; i < content.length && braceCount > 0; i++) {
382
+ if (content[i] === "{") braceCount++;
383
+ else if (content[i] === "}") braceCount--;
384
+ rlsEnd = i;
385
+ }
386
+ const rlsContent = content.slice(rlsStart, rlsEnd);
387
+ const enabledMatch = rlsContent.match(/enabled\s*:\s*(true|false)/);
388
+ const enabled = enabledMatch ? enabledMatch[1] === "true" : false;
389
+ const defaultDenyMatch = rlsContent.match(/defaultDeny\s*:\s*(true|false)/);
390
+ const defaultDeny = defaultDenyMatch ? defaultDenyMatch[1] === "true" : true;
391
+ const policies = [];
392
+ const policiesMatch = rlsContent.match(/policies\s*:\s*\[/);
393
+ if (policiesMatch) {
394
+ const pStart = rlsContent.indexOf(policiesMatch[0]) + policiesMatch[0].length;
395
+ let bracketCount = 1;
396
+ let pEnd = pStart;
397
+ for (let i = pStart; i < rlsContent.length && bracketCount > 0; i++) {
398
+ if (rlsContent[i] === "[") bracketCount++;
399
+ else if (rlsContent[i] === "]") bracketCount--;
400
+ pEnd = i;
401
+ }
402
+ const policiesContent = rlsContent.slice(pStart, pEnd);
403
+ const policyRegex = /\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/g;
404
+ let policyMatch;
405
+ while ((policyMatch = policyRegex.exec(policiesContent)) !== null) {
406
+ const pBody = policyMatch[1];
407
+ const name = pBody.match(/name\s*:\s*['"]([^'"]+)['"]/)?.[1];
408
+ const operation = pBody.match(/operation\s*:\s*['"]([^'"]+)['"]/)?.[1];
409
+ const role = pBody.match(/role\s*:\s*['"]([^'"]+)['"]/)?.[1];
410
+ if (!name || !operation) continue;
411
+ const usingExpr = extractJsonExpression(pBody, "using");
412
+ const checkExpr = extractJsonExpression(pBody, "check");
413
+ policies.push({ name, operation, using: usingExpr, check: checkExpr, role });
414
+ }
415
+ }
416
+ if (!enabled && policies.length === 0) return void 0;
417
+ return { enabled, defaultDeny, policies };
418
+ }
419
+ function extractJsonExpression(text, key) {
420
+ const keyRegex = new RegExp(`${key}\\s*:\\s*\\{`);
421
+ const keyMatch = text.match(keyRegex);
422
+ if (!keyMatch) return void 0;
423
+ const start = text.indexOf(keyMatch[0]) + keyMatch[0].length;
424
+ let braceCount = 1;
425
+ let end = start;
426
+ for (let i = start; i < text.length && braceCount > 0; i++) {
427
+ if (text[i] === "{") braceCount++;
428
+ else if (text[i] === "}") braceCount--;
429
+ end = i;
430
+ }
431
+ const raw = "{" + text.slice(start, end) + "}";
432
+ try {
433
+ const jsonStr = raw.replace(/(\w+)\s*:/g, '"$1":').replace(/'/g, '"').replace(/,\s*([}\]])/g, "$1").replace(/"(\$\w+(?:\.\w+)?)"/g, '"$1"');
434
+ return JSON.parse(jsonStr);
435
+ } catch {
436
+ return void 0;
437
+ }
438
+ }
341
439
  function getColumnSqlType(type) {
342
440
  switch (type) {
343
441
  case "text":
@@ -1586,7 +1684,7 @@ async function runGenerate(spinner) {
1586
1684
  }
1587
1685
  isGenerating = true;
1588
1686
  try {
1589
- const { generateTypes: generateTypes2 } = await import("./generate-O66KTG5U.js");
1687
+ const { generateTypes: generateTypes2 } = await import("./generate-CQIJJTKG.js");
1590
1688
  spinner.text = "Regenerating types...";
1591
1689
  await generateTypes2({ silent: true });
1592
1690
  spinner.succeed("Types regenerated");
@@ -1806,7 +1904,7 @@ async function devCommand(options) {
1806
1904
  }
1807
1905
  }
1808
1906
  spinner.text = "Generating types...";
1809
- const { generateTypes: generateTypes2 } = await import("./generate-O66KTG5U.js");
1907
+ const { generateTypes: generateTypes2 } = await import("./generate-CQIJJTKG.js");
1810
1908
  await generateTypes2({ silent: true });
1811
1909
  spinner.succeed("Types generated");
1812
1910
  if (config.projectId) {
@@ -2141,12 +2239,12 @@ async function updateCommand(options) {
2141
2239
  const deps = packageJson.dependencies || {};
2142
2240
  const devDeps = packageJson.devDependencies || {};
2143
2241
  const installedDeps = [];
2144
- for (const pkg of TETHER_PACKAGES) {
2145
- if (deps[pkg] && !deps[pkg].startsWith("workspace:")) {
2146
- installedDeps.push({ name: pkg, current: deps[pkg], isDev: false });
2242
+ for (const pkg2 of TETHER_PACKAGES) {
2243
+ if (deps[pkg2] && !deps[pkg2].startsWith("workspace:")) {
2244
+ installedDeps.push({ name: pkg2, current: deps[pkg2], isDev: false });
2147
2245
  }
2148
- if (devDeps[pkg] && !devDeps[pkg].startsWith("workspace:")) {
2149
- installedDeps.push({ name: pkg, current: devDeps[pkg], isDev: true });
2246
+ if (devDeps[pkg2] && !devDeps[pkg2].startsWith("workspace:")) {
2247
+ installedDeps.push({ name: pkg2, current: devDeps[pkg2], isDev: true });
2150
2248
  }
2151
2249
  }
2152
2250
  if (installedDeps.length === 0) {
@@ -2158,19 +2256,19 @@ async function updateCommand(options) {
2158
2256
  `));
2159
2257
  const spinner = ora5("Checking for updates...").start();
2160
2258
  const packagesToUpdate = [];
2161
- for (const pkg of installedDeps) {
2162
- const latestVersion = await getLatestVersion2(pkg.name);
2259
+ for (const pkg2 of installedDeps) {
2260
+ const latestVersion = await getLatestVersion2(pkg2.name);
2163
2261
  if (latestVersion) {
2164
- const currentClean = pkg.current.replace(/^[\^~>=<]+/, "");
2262
+ const currentClean = pkg2.current.replace(/^[\^~>=<]+/, "");
2165
2263
  if (currentClean !== latestVersion) {
2166
2264
  packagesToUpdate.push({
2167
- name: pkg.name,
2265
+ name: pkg2.name,
2168
2266
  current: currentClean,
2169
2267
  latest: latestVersion,
2170
- isDev: pkg.isDev
2268
+ isDev: pkg2.isDev
2171
2269
  });
2172
2270
  } else {
2173
- console.log(chalk5.dim(` ${pkg.name}@${currentClean} (up to date)`));
2271
+ console.log(chalk5.dim(` ${pkg2.name}@${currentClean} (up to date)`));
2174
2272
  }
2175
2273
  }
2176
2274
  }
@@ -2180,9 +2278,9 @@ async function updateCommand(options) {
2180
2278
  return;
2181
2279
  }
2182
2280
  console.log(chalk5.cyan("\n Updates available:\n"));
2183
- for (const pkg of packagesToUpdate) {
2281
+ for (const pkg2 of packagesToUpdate) {
2184
2282
  console.log(
2185
- chalk5.white(` ${pkg.name}`) + chalk5.red(` ${pkg.current}`) + chalk5.dim(" \u2192 ") + chalk5.green(`${pkg.latest}`) + (pkg.isDev ? chalk5.dim(" (dev)") : "")
2283
+ chalk5.white(` ${pkg2.name}`) + chalk5.red(` ${pkg2.current}`) + chalk5.dim(" \u2192 ") + chalk5.green(`${pkg2.latest}`) + (pkg2.isDev ? chalk5.dim(" (dev)") : "")
2186
2284
  );
2187
2285
  }
2188
2286
  if (options.dryRun) {
@@ -2601,8 +2699,11 @@ function askConfirmation(prompt) {
2601
2699
  }
2602
2700
 
2603
2701
  // src/index.ts
2702
+ var __filename = fileURLToPath(import.meta.url);
2703
+ var __dirname = path8.dirname(__filename);
2704
+ var pkg = fs8.readJsonSync(path8.resolve(__dirname, "../package.json"));
2604
2705
  var program = new Command();
2605
- program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version("0.0.1").enablePositionalOptions();
2706
+ program.name("tthr").description("Tether CLI - Realtime SQLite for modern applications").version(pkg.version).enablePositionalOptions();
2606
2707
  program.command("init [name]").description("Create a new Tether project").option("-t, --template <template>", "Project template (vue, svelte, react, vanilla)", "vue").action(initCommand);
2607
2708
  program.command("dev").description("Start local development server with hot reload").option("-p, --port <port>", "Port to run on").option("-e, --env <environment>", "Target environment (default: development)").option("--local", "Run fully local (do not connect to cloud)").option("--skip-framework", "Only run Tether watchers, skip framework dev server").allowUnknownOption().passThroughOptions().action((options, command) => {
2608
2709
  const extraArgs = command.args || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tthr",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Tether CLI - project scaffolding and deployment",
5
5
  "type": "module",
6
6
  "bin": {