Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
# https://editorconfig.org/

root = true

Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

# compiled output
/dist/
/declarations/
/tmp/

# dependencies
Expand Down
4 changes: 1 addition & 3 deletions .stylelintrc.cjs → .stylelintrc.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
'use strict';

module.exports = {
export default {
extends: ['stylelint-config-standard'],
rules: {
'at-rule-no-deprecated': [true, { ignoreAtRules: ['/^view/', 'apply'] }],
Expand Down
6 changes: 4 additions & 2 deletions app/components/alpha-input.gts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ export default class AlphaInputComponent extends Component<AlphaInputSignature>
}

@action
isComplete(buffer: Buffer, opts: { regex: string }): boolean {
isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
const value = buffer.join('');

return Boolean(value.length) && new RegExp(opts.regex).test(value);
return (
Boolean(value.length) && new RegExp(opts.regex as string).test(value)
);
Comment on lines +58 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the file
fd "alpha-input" --type f

Repository: shipshapecode/swach

Length of output: 93


🏁 Script executed:

# Read the alpha-input.gts file to see the actual code
cat -n app/components/alpha-input.gts

Repository: shipshapecode/swach

Length of output: 3154


🏁 Script executed:

# Search for other uses of isComplete
rg "isComplete" --type ts --type gts -B 2 -A 2

Repository: shipshapecode/swach

Length of output: 90


🏁 Script executed:

# Search for isComplete with proper file types
rg "isComplete" -B 2 -A 2

Repository: shipshapecode/swach

Length of output: 1711


🏁 Script executed:

# Let's look at rgb-input.gts and hex-input.gts to see the pattern
cat -n app/components/rgb-input.gts | head -100

Repository: shipshapecode/swach

Length of output: 3231


🏁 Script executed:

# Check hex-input.gts
cat -n app/components/hex-input.gts | head -80

Repository: shipshapecode/swach

Length of output: 2796


🏁 Script executed:

# Test what happens with new RegExp(undefined)
node -e "console.log(new RegExp(undefined)); console.log(new RegExp(undefined).test('hello'));"

Repository: shipshapecode/swach

Length of output: 74


🏁 Script executed:

# Check if regex is always passed in the options, or could be undefined
rg "OneWayInputMask" -A 15 | grep -E "(regex|options)"

Repository: shipshapecode/swach

Length of output: 392


Fix type annotation and unsafe regex cast in completion validation.

The opts parameter is typed as { regex?: string } but receives a RegExp instance (line 38). When casting as string with opts.regex as string, if regex were undefined, new RegExp(undefined) would create /(?:)/, matching any input unexpectedly.

Update the type signature to accept RegExp | string | undefined, and handle each case explicitly with a fallback to this.alphaRegex:

Suggested fix
-  isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
+  isComplete(buffer: Array<string>, opts: { regex?: string | RegExp }): boolean {
     const value = buffer.join('');
-
-    return (
-      Boolean(value.length) && new RegExp(opts.regex as string).test(value)
-    );
+    const pattern =
+      opts.regex instanceof RegExp
+        ? opts.regex
+        : opts.regex
+          ? new RegExp(opts.regex)
+          : this.alphaRegex;
+
+    return Boolean(value.length) && pattern.test(value);
   }

Note: The same issue exists in rgb-input.gts (line 79) and hex-input.gts (line 54).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
const value = buffer.join('');
return Boolean(value.length) && new RegExp(opts.regex).test(value);
return (
Boolean(value.length) && new RegExp(opts.regex as string).test(value)
);
isComplete(buffer: Array<string>, opts: { regex?: string | RegExp }): boolean {
const value = buffer.join('');
const pattern =
opts.regex instanceof RegExp
? opts.regex
: opts.regex
? new RegExp(opts.regex)
: this.alphaRegex;
return Boolean(value.length) && pattern.test(value);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/alpha-input.gts` around lines 58 - 63, Update the isComplete
signature and logic so opts.regex accepts RegExp|string|undefined and you don't
cast to string: change the parameter type in isComplete to opts: { regex?:
RegExp | string } (or include undefined) and then detect if opts.regex is a
RegExp, a string, or missing; construct the test RegExp by using opts.regex
directly when it's already a RegExp, new RegExp(opts.regex) when it's a string,
and fall back to this.alphaRegex when undefined/invalid before calling
.test(value); apply the same pattern to the analogous methods in rgb-input.gts
and hex-input.gts to avoid unsafe new RegExp(undefined) behavior.

}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/components/hex-input.gts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export default class HexInputComponent extends Component<HexInputSignature> {
}

@action
isComplete(buffer: Buffer, opts: { regex: string }): boolean {
return new RegExp(opts.regex).test(buffer.join(''));
isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
return new RegExp(opts.regex as string).test(buffer.join(''));
Comment on lines +54 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the hex-input.gts file
find . -name "hex-input.gts" -type f

Repository: shipshapecode/swach

Length of output: 93


🏁 Script executed:

# Also search for any references to ember-inputmask in the codebase
rg "ember-inputmask" --type ts --type js --type gts -A 3 -B 3

Repository: shipshapecode/swach

Length of output: 90


🏁 Script executed:

# Check the package.json to confirm the version of ember-inputmask
cat package.json | grep -A 2 -B 2 "inputmask"

Repository: shipshapecode/swach

Length of output: 237


🏁 Script executed:

# Read the hex-input.gts file to see the isComplete method
cat -n ./app/components/hex-input.gts

Repository: shipshapecode/swach

Length of output: 3079


🏁 Script executed:

# Search for isComplete usage in the codebase to understand how it's called
rg "isComplete" -g "*.gts" -g "*.ts" -g "*.js" -A 5 -B 5

Repository: shipshapecode/swach

Length of output: 3806


🌐 Web query:

ember-inputmask v4.0.3 isComplete callback signature opts.regex documentation

💡 Result:

In Inputmask (the library wrapped by ember-inputmask), the isComplete option is a hook you can override with this signature:

  • isComplete(buffer: string[], opts: Options): boolean [1]

When you use the "Regex" mask/alias, the regex you configured is available as:

  • opts.regex (a string) [1]

The Inputmask docs/examples show opts.regex being used exactly this way:

$(selector).inputmask("Regex", {
  regex: "[0-9]*",
  isComplete: function (buffer, opts) {
    return new RegExp(opts.regex).test(buffer.join(""));
  }
});
``` [2]

**Sources:** [1] Type definitions for Inputmask options/signatures, including `isComplete(buffer, opts)` and `opts.regex`. [2] Inputmask documentation example showing `isComplete(buffer, opts)` using `opts.regex`.

---

🏁 Script executed:

```shell
# Let's verify the behavior of RegExp with undefined
node << 'EOF'
// Test what happens when RegExp is constructed with undefined
try {
  const result = new RegExp(undefined);
  console.log("RegExp(undefined):", result);
  console.log("Test 'undefined' string:", result.test("undefined"));
  console.log("Test other string:", result.test("hello"));
} catch (e) {
  console.log("Error:", e.message);
}
EOF

Repository: shipshapecode/swach

Length of output: 142


Guard optional opts.regex before building RegExp.

Line 55 force-casts an optional value (opts.regex as string) without checking if it's defined. If opts.regex is undefined, new RegExp(undefined) creates the permissive regex /(?:)/ that matches any string, causing completion checks to incorrectly succeed on invalid input.

Additionally, the type annotation declares regex?: string, but the component passes regex=this.hexRegex where hexRegex is a RegExp object, creating a type mismatch masked by the cast.

Suggested fix
-  isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
-    return new RegExp(opts.regex as string).test(buffer.join(''));
+  isComplete(buffer: Array<string>, opts: { regex?: string | RegExp }): boolean {
+    const pattern =
+      opts.regex instanceof RegExp
+        ? opts.regex
+        : opts.regex
+          ? new RegExp(opts.regex)
+          : this.hexRegex;
+
+    return pattern.test(buffer.join(''));
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
return new RegExp(opts.regex as string).test(buffer.join(''));
isComplete(buffer: Array<string>, opts: { regex?: string | RegExp }): boolean {
const pattern =
opts.regex instanceof RegExp
? opts.regex
: opts.regex
? new RegExp(opts.regex)
: this.hexRegex;
return pattern.test(buffer.join(''));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/hex-input.gts` around lines 54 - 55, The isComplete method
currently force-casts opts.regex to string and constructs a RegExp
unconditionally; instead, guard for undefined and accept a RegExp instance:
update the parameter type (regex?: string|RegExp) or handle both cases inside
isComplete, return false if opts.regex is not provided, and when provided, if
it's already a RegExp use it directly, otherwise construct a RegExp from the
string; reference the isComplete method and opts.regex and the component prop
hexRegex (which is a RegExp) so the fix prevents new RegExp(undefined) and
resolves the string/RegExp mismatch.

}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/components/rgb-input.gts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ export default class RgbaInputComponent extends Component<RgbaInputSignature> {
}

@action
isComplete(buffer: Buffer, opts: { regex: string }): boolean {
isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
const value = buffer.join('');

return Boolean(value.length) && new RegExp(opts.regex).test(value);
return Boolean(value.length) && new RegExp(opts.regex as string).test(value);
Comment on lines +79 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the file
find . -name "rgb-input.gts" -type f

Repository: shipshapecode/swach

Length of output: 93


🏁 Script executed:

# Let's examine the rgb-input.gts file
cat -n ./app/components/rgb-input.gts

Repository: shipshapecode/swach

Length of output: 4024


🏁 Script executed:

# Let's check the ember-inputmask package.json or dependency info
find . -name "package.json" -type f -exec grep -l "ember-inputmask" {} \;

Repository: shipshapecode/swach

Length of output: 77


🏁 Script executed:

# Let's check the package.json for ember-inputmask version
cat ./package.json | grep -A 2 -B 2 "ember-inputmask"

Repository: shipshapecode/swach

Length of output: 237


🌐 Web query:

ember-inputmask v4.0.3 isComplete callback opts.regex parameter

💡 Result:

In Inputmask v4.0.3 (which ember-inputmask wraps), the isComplete hook is called as:

  • isComplete(buffer, opts) -> boolean
  • opts is the resolved Inputmask options object for the current mask
  • When you use the "Regex" mask/alias and pass a regex option, that value is available as opts.regex inside isComplete (so you can re-test the buffer against the configured regex). Example from the Inputmask docs: new RegExp(opts.regex).test(buffer.join('')). [1]

Source

  1. Inputmask docs / README section “isComplete” showing Args => buffer, opts and the "Regex" example using opts.regex. [1]

🌐 Web query:

ember-inputmask OneWayInputMask isComplete callback options parameter passed

💡 Result:

In ember-inputmask, the OneWayInputMask component is just passing your config through to Inputmask, so the isComplete hook uses Inputmask’s native signature. The 2nd parameter (options / opts) is the resolved Inputmask options object for that mask instance (your passed options merged with defaults/alias options, etc.). [1][2]

Signature:

isComplete(buffer, opts) { ... }
  • buffer: the current mask buffer (array of characters)
  • opts: the Inputmask options object (mask/alias + merged defaults/options) [2]

Example:

<Inputmask
  `@mask`="99/99/9999"
  `@isComplete`={{(buffer, opts) => {
    // opts.mask, opts.placeholder, opts.clearIncomplete, opts.alias, etc.
    return buffer.join('').indexOf('_') === -1;
  }}}
/>

Sources:
[1] https://github.com/brandynbennett/ember-inputmask
[2] https://github.com/trendyminds/jquery.inputmask (docs excerpt showing isComplete(buffer, opts))


🌐 Web query:

Inputmask v4.0.3 isComplete callback opts.regex type RegExp string

💡 Result:

In Inputmask 4.x (including v4.0.3), the Regex/regex option is treated as a string pattern, so inside callbacks like isComplete(buffer, opts) you should expect opts.regex to be a string, not a RegExp object. The docs’ own example builds a RegExp from that string: new RegExp(opts.regex). [1]

If you start with a RegExp, pass its .source (and handle flags yourself), e.g.:

const re = /^[0-9]*$/i;

$(el).inputmask("Regex", {
  regex: re.source,                 // <- string
  isComplete(buffer, opts) {
    return new RegExp(opts.regex, re.flags).test(buffer.join(""));
  }
});

@types/inputmask also models regex?: string, matching this behavior. [2]

Sources: [1] [2]


🏁 Script executed:

# Let's search for other uses of isComplete in the codebase to understand the pattern
rg "isComplete" -A 3 -B 3

Repository: shipshapecode/swach

Length of output: 2341


🏁 Script executed:

# Let's examine the alpha-input.gts to see how it implements isComplete
cat -n ./app/components/alpha-input.gts | sed -n '1,100p'

Repository: shipshapecode/swach

Length of output: 3154


Add fallback regex handling for optional opts.regex parameter.

The isComplete method declares opts.regex as optional but the component always provides it. However, the type signature and the force cast create ambiguity. The current code lacks a fallback if opts.regex is ever undefined—new RegExp(undefined as string) would not validate RGB values correctly.

Suggested fix
-  isComplete(buffer: Array<string>, opts: { regex?: string }): boolean {
+  isComplete(buffer: Array<string>, opts: { regex?: string | RegExp }): boolean {
     const value = buffer.join('');
-
-    return Boolean(value.length) && new RegExp(opts.regex as string).test(value);
+    const pattern =
+      opts.regex instanceof RegExp
+        ? opts.regex
+        : opts.regex
+          ? new RegExp(opts.regex)
+          : this.rgbRegex;
+
+    return Boolean(value.length) && pattern.test(value);
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/rgb-input.gts` around lines 79 - 82, The isComplete method
currently force-casts opts.regex and builds a RegExp from it which fails if
opts.regex is undefined; update isComplete to use a safe fallback pattern when
opts.regex is missing (e.g., a regex string that matches three comma-separated
integers for RGB), e.g. compute const pattern = opts.regex ?? '...default
pattern...' and then use new RegExp(pattern).test(buffer.join('')) so isComplete
uses opts.regex when provided and a robust default otherwise; reference the
isComplete(buffer, opts) function and the opts.regex parameter when making the
change.

}

/**
Expand Down
Empty file removed app/services/.gitkeep
Empty file.
14 changes: 8 additions & 6 deletions babel.config.cjs → babel.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const {
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

import {
babelCompatSupport,
templateCompatSupport,
} = require('@embroider/compat/babel');
} from '@embroider/compat/babel';

module.exports = {
export default {
plugins: [
[
'@babel/plugin-transform-typescript',
Expand All @@ -16,7 +19,6 @@ module.exports = {
[
'babel-plugin-ember-template-compilation',
{
compilerPath: 'ember-source/dist/ember-template-compiler.js',
enableLegacyModules: [
'ember-cli-htmlbars',
'ember-cli-htmlbars-inline-precompile',
Expand All @@ -29,14 +31,14 @@ module.exports = {
'module:decorator-transforms',
{
runtime: {
import: require.resolve('decorator-transforms/runtime-esm'),
import: import.meta.resolve('decorator-transforms/runtime-esm'),
},
},
],
[
'@babel/plugin-transform-runtime',
{
absoluteRuntime: __dirname,
absoluteRuntime: dirname(fileURLToPath(import.meta.url)),
useESModules: true,
regenerator: false,
},
Expand Down
2 changes: 1 addition & 1 deletion config/ember-cli-update.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packages": [
{
"name": "@ember/app-blueprint",
"version": "6.8.3",
"version": "6.11.0",
"blueprints": [
{
"name": "@ember/app-blueprint",
Expand Down
35 changes: 15 additions & 20 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
* npx eslint --inspect-config
*
*/
import babelParser from '@babel/eslint-parser';
import babelParser from '@babel/eslint-parser/experimental-worker';
import js from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import ember from 'eslint-plugin-ember/recommended';
import n from 'eslint-plugin-n';
import qunit from 'eslint-plugin-qunit';
import { defineConfig, globalIgnores } from 'eslint/config';
import globals from 'globals';
import ts from 'typescript-eslint';

Expand All @@ -34,29 +35,20 @@ const parserOptions = {
},
};

export default ts.config(
export default defineConfig([
globalIgnores([
'.vite/**/*',
'dist/',
'coverage/',
'electron-app/dist/**/*',
'out/**/*',
'!**/.*',
]),
js.configs.recommended,
ember.configs.base,
ember.configs.gjs,
ember.configs.gts,
eslintConfigPrettier,
/**
* Ignores must be in their own object
* https://eslint.org/docs/latest/use/configure/ignore
*/
{
ignores: [
'.vite/**/*',
'coverage/**/*',
'declarations/**/*',
'dist/**/*',
'electron-app/dist/**/*',
'node_modules/**/*',
'out/**/*',
'types/**/*',
'!**/.*',
],
},
/**
* https://eslint.org/docs/latest/use/configure/configuration-files#configuring-linter-options
*/
Expand Down Expand Up @@ -85,6 +77,9 @@ export default ts.config(
languageOptions: {
parser: ember.parser,
parserOptions: parserOptions.esm.ts,
globals: {
...globals.browser,
},
},
extends: [...ts.configs.recommendedTypeChecked, ember.configs.gts],
rules: {
Expand Down Expand Up @@ -147,4 +142,4 @@ export default ts.config(
},
},
},
);
]);
17 changes: 7 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint . --cache",
"lint:js:fix": "eslint . --fix",
"lint:types": "glint --build",
"lint:types": "ember-tsc --noEmit",
"make": "cross-env EMBER_CLI_ELECTRON=true electron-forge make",
"package": "cross-env EMBER_CLI_ELECTRON=true electron-forge package",
"publish": "electron-forge publish",
Expand Down Expand Up @@ -75,7 +75,7 @@
"ember-element-helper": "^0.8.8",
"ember-event-helpers": "^0.1.1",
"ember-inflector": "^6.0.0",
"ember-inputmask": "^4.0.1",
"ember-inputmask": "^4.0.3",
"ember-local-storage": "2.0.6",
"ember-math-helpers": "^5.0.0",
"ember-modifier": "^4.3.0",
Expand All @@ -88,8 +88,7 @@
"hue-hunter": "^0.4.1",
"indexeddb-export-import": "^2.1.5",
"menubar": "^9.5.2",
"throttle-debounce": "^5.0.2",
"tracked-built-ins": "^4.1.0"
"throttle-debounce": "^5.0.2"
},
"devDependencies": {
"@babel/core": "^7.29.0",
Expand Down Expand Up @@ -119,10 +118,9 @@
"@embroider/router": "^3.0.6",
"@embroider/vite": "^1.6.0",
"@eslint/js": "^9.39.3",
"@glint/core": "^1.5.2",
"@glint/environment-ember-loose": "^1.5.2",
"@glint/environment-ember-template-imports": "^1.5.2",
"@glint/template": "~1.5.2",
"@glint/ember-tsc": "^1.1.1",
"@glint/template": "~1.7.4",
"@glint/tsserver-plugin": "^2.1.0",
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
"@release-it-plugins/lerna-changelog": "^8.0.1",
"@rollup/plugin-babel": "^6.1.0",
Expand All @@ -135,13 +133,12 @@
"@types/qunit": "^2.19.13",
"@types/sinon": "^21.0.0",
"@vitest/ui": "^4.0.18",
"babel-plugin-ember-template-compilation": "^2.4.1",
"babel-plugin-ember-template-compilation": "^3.1.0",
"babel-preset-env": "^1.7.0",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^9.2.1",
"cross-env": "^10.1.0",
"electron": "^40.6.1",
"ember-auto-import": "^2.12.1",
"ember-cli": "^6.11.0",
"ember-cli-babel": "^8.3.1",
"ember-cli-clean-css": "^3.0.0",
Expand Down
Loading
Loading