squared-express

3.1.6 • Public • Published

squared-express 3.1

  • NodeJS 14 (Minimum)

NPX

> npm i squared-express

> cp node_modules/squared-express/config/json/* .
> squared.[json|yml|cjs] # configure

> npx serve [--help]

> http://localhost:3000

Local Development

# NOTE: cd ./dist

> squared.[json|yml|cjs] # configure
> node serve.js [--help]

# OR - Unix

> chmod +x serve.js # once
> ./serve.js [--help]

Typically you will be using squared 4/5 although it can be used independently for server or debugging purposes. It is recommended to copy the latest release into your squared project folder when there are patches.

Version Compatibility

* v1.0.0 - 04-29-21 - squared-functions 1.0 (squared 3.0 + NodeJS 10)
* v1.1.0 - 06-16-21 - squared-functions 1.1 (squared 3.1)
* v1.2.0 - 08-03-21 - squared-functions 1.2
* v1.3.0 - 09-15-21 - squared-functions 2.0 (squared 3.2)
* v1.4.0 - 10-18-21 - squared-functions 3.0 (squared 3.3)
* v1.5.0 - 12-07-21 - squared-functions 3.1
* v1.7.0 - 12-23-21 - squared-functions 3.2 (squared 3.4)
* v1.8.0 - 01-07-22 - squared-functions 3.3 (squared 3.5)
* v1.9.0 - 02-03-22 - squared-functions 3.5
* v2.0.0 - 02-22-22 - squared-functions 4.0 (squared 3.6 + NodeJS 12)
* v2.1.0 - 04-29-22 - squared-functions 4.1 (squared 3.7)
* v2.2.0 - 06-16-22 - squared-functions 4.2 (squared 4.0)
* v2.3.0 - 08-06-22 - squared-functions 4.3 (squared 4.1)
* v2.4.0 - 09-21-22 - squared-functions 4.4 (squared 4.2)
* v2.5.0 - 10-02-22 - squared-functions 4.5 (squared 4.2.2)
* v2.6.0 - 10-13-22 - squared-functions 4.6 (squared 4.2.3)
* v2.7.0 - 10-28-22 - squared-functions 4.7 (squared 4.3)
* v2.8.0 - 11-05-22 - squared-functions 4.8 (squared 4.3.1)
* v2.9.0 - 12-31-22 - squared-functions 4.9 (squared 4.4)
* v2.9.5 - 02-13-23 - squared-functions 4.9.5 (squared 4.5)
* v3.0.0 - 04-29-23 - E-mc 0.5.2 (squared 5.0 + NodeJS 14)
* v3.1.0 - 08-09-23 - E-mc 0.6.0 (squared 5.1)
* v3.1.1 - 12-07-23 - E-mc 0.7.0 (squared 5.1.1)
* v3.1.2 - 12-23-23 - E-mc 0.8.0 (squared 5.1.2)
* v3.1.3 - 01-26-24 - E-mc 0.8.2 (squared 5.1.3)
* v3.1.4 - 02-19-24 - E-mc 0.8.4 (squared 5.1.5)
* v3.1.5 - 03-06-24 - E-mc 0.8.6 (squared 5.1.6)
* v3.1.6 - 04-02-24 - E-mc 0.8.7 (squared 5.1.7)

Minor releases are sometimes released in conjunction with squared patch releases. Using squared-express with an older version of E-mc/squared-functions is not recommended.

Request Options

// All attributes are optional

const data = {
    // Archive
    filename: "archive1",
    format: "zip", // zip | tar | gz/tgz + [ 7z | wbn | zopfli -> gz ]
    copyTo: "/path/project", // zip from directory

    // Copy
    emptyDir: false, // Empty target directory
    watch: false, // Enable file watching
    update: { // Reload assets and data sources
      interval: 10 * 60, // 10m (by second) (required)
      interval: "1d",
      id: "111-111-111" | location.href, // Overwrite previous request with same ID (optional)
      start: "Jan 01 2023 12:00:00" | "1w 1d 1h 1m 1s 1ms", // Empty is immediate (optional)
      expires: "Jan 01 2023 12:00:00" | "1w 1d 1h 1m 1s 1ms" // Empty is never (optional)
    },
    update: true, // Uses URL and glob values in "node.tasks.copy:update"

    incremental: false, // Explicit "false" to disable
    incremental: "exists", // Will bypass files already located at destination
    incremental: "etag", // Same as "exists" except HTTP downloads will verify ETag
    incrementalMap: {
      pathname: {
        "images/": "exists", // Not recursive
        "js/**/*": "etag" // Glob is supported
      },
      extension: {
        "js": "etag",
        "mjs": false
      },
      mime: {
        "image/png": "exists", // First match will quit search
        "image/*": "etag"
      },
      overwrite: false // Only when undefined
    },

    checksum: true, // sha256 + recursive
    checksum: "sha512", // checksum.sha512
    checksum: "filename.sha384", // sha384
    checksum: {
      algorithm: "md5", // Default is "sha256"
      digest: "base64", // Default is "hex"
      filename: "checksum.crc", // Default is "checksum" + algorithm
      recursive: true, // Default is "false"
      recursive: 1, // Ignore nested checksum files
      include: "**/*.png", // Has precedence
      exclude: ["**/*.js", "**/*.css"]
    },

    assets: [ // Undetected resources used in the application (e.g. imports inside custom elements or SVG)
      {
        pathname: "app/src/main/res/drawable",
        filename: "ic_launcher_background.xml",
        uri: "http://localhost:3000/common/images/ic_launcher_background.xml"
      }
    ],
    filter: (asset, index) => asset.mimeType === "text/html" || asset.filename.endsWith(".xml"), // Include only "text/html" and XML files
    exclusions: {
      glob: ["**/*.zip"],
      pathname: ["app/build", "app/libs"],
      filename: ["ic_launcher_foreground.xml"],
      extension: ["iml", "pro"], // Not case-sensitive
      pattern: ["output", /grad.+?\./i, "\\.git"]
    },
    exclusions: ["**/*.zip", /\.zip$/], // glob + pattern (uses glob for strings)
    modules: ["db", "cloud"], // Attempt to install undetected modules

    config: {
      uri: "http://localhost:3000/example.yml" // Auto-detect "yml" extension
    },
    config: {
      mimeType: "json" // http://hostname/example.html -> http://hostname/example.html.json
    },
    config: {
      uri: "http://hostname/example.config",
      mimeType: "text/javascript", // json
      cache: true // Use when config file is unchanged
    },
    config: "http://hostname/example.yml",
    config: "json", // json | yml | yaml
    config: true, // Uses sqd.config in base directory
    config: {
      uri: true, // sqd.config
      inherit: true // Uses glob matching for multiple blocks
    },
    config: {
      uri: "http://hostname/example.json",
      key: "111-111-111" // Key inside map
    },

    // Settings overrides
    cache: {
      request: true | ["http://localhost:3000"] // true & exclude (request.cache)
    },
    error: { // error.abort
      abort: ["filemanager", "watch", "cloud", "jimp", "gulp", "android", "chrome"], // By module name (customizable)
      abort: ["(http)", "(process)", "(image)", "(compress)", "(cloud)", "(watch)", "(system)", "(node)", "(file)", "(permission)", "(exec)", "(timeout)"], // By error type (module takes precedence)
      abort: ["chrome", "cloud"], // Only "chrome" or "cloud" module errors are aborted
      abort: ["filemanager", "(http)", "chrome"], // "http" and "chrome" errors will bubble up to "filemanager" and abort entire transaction (except watch)
      abort: ["filemanager", "(exec)", "chrome"], // Required for NPM auto-install (use with "filemanager" for auto-restart)
      abort: "filemanager", // Only for permissions
      abort: "(unknown)", // Abort all errors
      fatal: true, // Abort all thrown errors
      fatal: false // Overrides system settings
    },

    broadcast: (result: { value: string, type?: NumString, timeStamp?: number }) => void,
    broadcast: {
      socketId: "111-111-111" | ["111-111-111", "222-222-222"],
      callback: function(result) { console.log(result.value); },
      port: 3443, // Optional
      secure: true
    },
    broadcastId: "111-111-111", // Reuse socket for simultaneous request or redirect messages to another destination

    log: false, // enabled (default is "true")
    log: "chrome" | ["cloud", "gulp"], // exclude
    log: {
      enabled: false,
      level: 0, // Modules will see all logs except when specified by user
      level: "debug", // fatal = 1, error = 2, warn = 3, info = 4, debug = 5, assert = 6, trace = 7
      exclude: ["cloud", "gulp"],
      useColor: true, // Includes broadcast messages
      showSize: false, // File size (default is "true")
      useNumeric: true
    },
    ignoreExtensions: true | string[], // Module name | NPM package

    // Auth
    outgoing: {
      authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InNxdWFyZWQiLCJwYXNzd29yZCI6Imp3dDEyMyIsImlhdCI6MTY0NTQxMTA1NX0.vK1VMoJNEirWhVjAH4V5VN21gebUtylqMi63gBKmRZM" // JSON web token (https://jwt.io)
    },

    // FileManager
    timeout: {
      filemanager: "1m 1s 1ms", // processTimeout
      jimp: "5s" // document + image + task (moduleName)
    },
    requestTimeout: 10, // request.read_timeout (seconds)
    headers: {
      "https://www.googleapis.com/v1/": { "authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" }, // request.headers (merged)
      "https://www.googleapis.com/v2/": { "authorization": "Bearer YOUR_ACCESS_TOKEN" }  // URLs are matched using length comparison
    },
    httpVersion: 1 | 2, // request.use.http_version (override)
    ipVersion: 0 | 4 | 6, // request.dns.family

    // Android
    manifest: {}, // ManifestData (types/android/resource.d.ts)
    projectName: "com.example.sqd", // rootProject.name (settings.gradle)
    namespace: "sqd1",  // android.defaultConfig.applicationId (app/build.gradle)
    javaVersion: 1.8 | 11, // JavaVersion.VERSION_1_8 | JavaVersion.VERSION_11 (outputDocumentEditing: true)
    targetAPI: 32 | "Tiramisu", // Override targetAPI (outputDocumentEditing: true)
    mainParentDir: "app", // Override "outputDirectory" (outputDocumentEditing: true)
    mainSrcDir: "src/main",
    mainActivityFile: "MainActivity.java", // "MainActivity.*" | "/user/project/path_to/MainActivity.java" | "app/path_to/MainActivity.java"
    dependencyScopes: true,
    dependencyScopes: "compile" | "provided" | "runtime" | "test", // implementation | compileOnly | runtimeOnly | testImplementation
    dependencyScopes: "snapshot", // Use latest published release
    dependencyScopes: ["snapshot", "compile"],
    versionName: "1.0",
    versionCode: 1,
    profileable: true, // <profileable android:enabled="[false|true]" />
    profileable: "release", // buildTypes.signingConfig signingConfigs.release
    profileable: "--warn-manifest-validation", // aaptOptions.additionalParameters (single --arg)
    profileable: ["release", "--warn-manifest-validation", "--no-version-vectors"], // buildTypes.signingConfig + aaptOptions.additionalParameters (multiple array)
    commands: "build" | ["test", "deploy"] | ["lint", ["test", "--rerun-tasks"]], // gradlew build | gradlew test deploy | gradlew lint && gradlew test --rerun-tasks
    extensionData: {}, // Transferred into AndroidDocument.extensionData
    updateXmlOnly: false,

    // Chrome
    cache: {
      transform: false, // Not recommended when using watch
      transform: true, // "etag" (not bundled) + string comparison by URL (single page)
      transform: "etag", // request.cache OR request.buffer.expires (required)
      transform: "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512", // Multi-[user|page] + Inline content (includes "etag")
      transform: { expires: "2h" }, // Expires in 2 hrs since creation
      transform: { expires: "1h", renew: true }, // Expires from 1 hr of last time accessed
      transform: { algorithm: "md5" /* etag */, expires: "2h", limit: "5mb" }, // Set expiration and content size limit
      transform: { exclude: { html: "*", js: ["bundle-es6"] } }, // Format names per type
      transform: { include: { css: "*", js: ["bundle"] } }
    },
    imports: {
      "http://localhost:3000/build/": "./build", // Starts with "http"
      "http://localhost:3000/dist/chrome.framework.js": "/path/project/build/framework/chrome/src/main.js" // Full file path
    },
    excluding: [document.getElementId("img")], // Elements to remove from HTML
    downloadOnly: true, // Do not transform HTML and CSS files
    webBundle: {
      baseUrl: "http://hostname/dir/", // Resolves to current host and directory
      rewriteHtmlPage: true | "index.html", // Hide or rename main page
      excludeHtmlPage: true, // Exclude HTML page from WBN archive
      excludeTransforms: true, // Exclude transformed files not used in HTML page
      includeScopes: ["**/*.css"], // http://localhost:3000/dir/**/*.css (hides "excludeTransforms" + "excludeScopes")
      excludeScopes: ["/**/*.js"], // http://localhost:3000/**/*.js
      copyTo: "/path/project", // Copy archive (absolute + permission)
      rootDirAlias: "__serverroot__" // Internal value
    },
    baseHref: "http://hostname/prod/example.html" // Additional hostname to use for parsing (URL | string)
};

// Project based - Android
squared.save(); // Uses defaults from settings

squared.saveAs("archive1.zip", data); // "data" (optional)
squared.appendTo("/path/project.zip");
squared.copyTo("/path/project");
squared.copyTo(["/path/project1", "/path/project2"]);

// File based - Chrome
squared.saveFiles("archive.7z", data); // "data" (required)
squared.appendFiles("http://hostname/project.zip", data);
squared.copyFiles("/path/www", data);
squared.copyFiles(["/server1/www", "/server2/www"], data);

You can also store your entire configuration in a single JSON/YAML file.

// http://hostname/example.html.json

{
  "emptyDir": true,
  "watch": true,
  "elements": [
    {
      "selector": "html",
      "type": "html",
      "filename": "index.html",
      "attributes": {
        "lang": "en"
      }
    }
  ]
}

squared.copyTo("/path/project", { config: "http://hostname/example.html.json" });

squared.copyTo("/path/project", { config: "json" }); // http://hostname/example.html

Archiving

Supported formats:

* zip
* tar
* gz/tgz

Optional formats:

* 7z
  - npm: node-7z + 7zip-bin
  - windows: https://www.7-zip.org/download.html
  - macos: https://formulae.brew.sh/formula/p7zip
  - linux: p7zip
* wbn
  - npm: wbn
* zopfli
  - npm: @gfx/zopfli (wasm)
  - npm: node-zopfli (python + gcc)

You can use a locally installed "7zip-bin" by providing the full location of the binary (7za or 7z.exe) or using the value "detect" in settings. (compress.7z.bin)

Routing

Simple routing and middleware can be loaded using locally evaluated functions in case you need additional functionality. It is not recommended to use this package in production environments when custom routes are defined.

https://expressjs.com/en/guide/routing.html

// squared.[json|yml|cjs]

{
  "apiVersion": false | "0.0.0", // Disable API routes
  "routing": {
    "common": [
      { "mount": "html", "path": "/", "options": { "setHeaders": "function (res, path, stat) { if (path.endsWith('.wbn')) res.set('X-Content-Type-Options', 'nosniff'); }" } }, // Static routes only - ServeStaticOptions
      { "mount": "dist", "path": "/dist" },
      { "get": "/index.html", "handler": "./index-html.js" }, // Handlers can be absolute paths
      { "all": "/route/pathname", "handler": ["./handler-1.js", "./handler-2.js", "npm:custom-package"] }, // "npm:" is recommended
      { "handler": "./middleware.cjs" }, // ".cjs" uses module.exports and is debuggable
      /* Middleware - higher-order functions */
      { "handler": "npm:cookie-parser", "apply": ["secret", { "encode": true }] }, // "apply" is always an array (required)
      { "get": "/session/log", "handler": ["npm:cookie-parser", "./handler.js", "npm:morgan"], "apply": { "npm:cookie-parser": [], "npm:morgan": ["combined"] }, // "./handler.js" is used directly
      /* List directory - https://github.com/expressjs/serve-index#options - npm i serve-index */
      { "mount": "public/images", "path": "/images", "static": true, "index": true | { "filter": "function (filename, index, files, dir) { return filename.endsWith('.png'); }", "stylesheet": "common/directory.css" } } // "static" is false will not serve directory contents
    ],
    "router": [
      { "router": true, "path": "/assets", "options": { "caseSensitive": true }, "handler": ["npm:cookie-parser", "npm:morgan"] }, // Middleware used with all requests under "/assets" (first) (required)
      { "router": "/assets", "path": "/js", "mount": "public/js", "handler": ["./handler-1.js", "./handler-2.js"], "index": true }, // Same as "common"
      { "router": "/assets", "path": "/css", "mount": "public/css", "handler": ["./handler-2.js", "./handler-3.js"] } // Path is "/assets/css"
    ],
    "production": [
      { "post": "/data/:userId", "handler": "function (req, res) { res.send(req.params); }" } // Inline handlers always start with "function" or "async function"
    ]
  },
  "error": {
    "handler": "function (err, data, require) { console.error(err); }", // Uncaught exceptions
    "out": "function (err, data, require) { require('fs').appendFileSync(require('path').resolve('./fail.log'), data.sessionId + ' ' + new Date(data.timeStamp).toISOString() + ': ' + err.stack + '\\n', 'utf-8'); }" // Caught exceptions + Global
  }
}

// index-html.js
function (req, res) {
    res.send("<html><body><!-- content --></body></html>");
}

// handler-1.js
function (req, res, next) {
    /* synchronous code */
    next();
}

// handler-2.js
async function (req, res, next) {
    /* await code */
    next(); // Express is not asynchronous
}

// npm i custom-package
const cookieParser = require("cookie-parser"); // npm i cookie-parser
module.exports = cookieParser(); // function (req, res, next) {}

// middleware.cjs
function (req, res, next) {
    const path = require("path");
}

// middleware.js
function (req, res, next, require) {
    /* Environment variables not accessible */
}
// serve.routes.js

module.exports = function(app: Express, settings: ExpressSettings, auth: IJwtAuth) {
    app.get("/path/url/1", (req, res) => {
        res.send("1");
    });

    app.get("/path/url/2", (req, res) => {
        res.send("2");
    });
};

Workspaces

Text based documents which require a preprocessor before being rendered can have the working live document precompiled. Images with transformations can similarly be served into the browser for immediate viewing during the drafting phase. It is not recommended for use in production deployments.

// squared.[json|yml|cjs]

{
  "routing": {
    "development": [
      // squared.setEndpoint("ASSETS_COPY", "/render")
      { "path": "/render", "document": "chrome", "type": "text/html" }, // Unsupported: UUID + cloud URL + inline + blob
      /* OR */
      { "path": "/render", "document": "chrome", "type": "function (asset, index, array) { return asset.bundleId > 0 && asset.mimeType?.endsWith('text/css'); }" }, // Array.filter

      { "mount": "../local/src", "path": "/workspace-1", "document": "chrome", "static": true }, // "static" enables loading external resources in same directory (e.g. source maps)
      { "mount": "../local/html", "path": "/workspace-2", "document": "chrome" }, // Without "document" it is treated as an ordinary static mount
      { "mount": "../local/html/common/images", "path": "/common/images", "image": "@pi-r/jimp" }, // NPM packages only with ImageConstructor or ImageV3Constructor interface (GET)
      /* OR */
      { "path": "/form-data/transform/image", "image": "@pi-r/jimp" } // Same (POST)
    ]
  }
}

NOTE: Script files with ".cjs" extension will be parsed with "require".

<html>
<head>
    <!-- predeclare ESM globals -->
    <script>var android = null;</script>

    <!-- ../local/build/main.js -->
    <script type="text/javascript" src="/workspace-1/build/main.js?format=bundle&type=js&{name}=app"></script> <!-- query params with (!name | {name}) are sent as external properties to transformer -->

    <!-- ../local/src/util.ts -->
    <script type="text/javascript" src="/workspace-1/src/util.ts?format=typescript&type=js&cache=1&{compilerOptions.target}=es2017"></script> <!-- nested query params use "qs" parsing library -->

    <!-- ../local/html/template-1.sass -->
    <link rel="stylesheet" type="text/css" href="/workspace-2/template-1.sass?format=demo&type=css&mime=text/css" /> <!-- "mime" might be required for non-standard file extensions -->

    <!-- ../local/html/css/template-2.sass -->
    <link rel="stylesheet" type="text/css" href="/workspace-2/css/template-2.sass?format=demo%2Bdemo-2&type=css&encoding=utf16le" /> <!-- "+" chain symbol (demo+demo-2) is URL encoded as "%2B" -->

    <!-- ../local/build/framework/android/src/main.js -->
    <script type="module">
        import appBase from "/build/framework/android/src/main.js?format=bundle-es6&type=js"; // Using cache not recommended when auxiliary files are modified
        android = appBase;
    </script>
</head>
<body>
    <img src="/common/images/android.png?command=webp(480x800)%7B90%7D" /> <!-- URL encoded: webp(480x800){90} (Only one rotation is supported) -->
    <!-- OR -->
    <img src="/common/images/android.png?command=webp(480x800)%7B90%7D&cache=1" /> <!-- uses disk storage (e.g. tmp/jimp) -->
</body>
</html>

NOTE: The optional "mime" parameter can be used when the server incorrectly detects the file content type.

If any of the required query parameters are missing then the request will be sent to the next Express static handler.

* Document: format + type + encoding? + cache?
* Image: [command | cmd | q] + cache?

You can debug TypeScript files directly with Visual Code using "tsc --[sourceMap|inlineSourceMap] --outDir <workspace>". It is also more efficient to use "--watch" in conjunction with "js" output files for recompilation.

NOTE: External properties are parsed with the same qs 6.11 library that is bundled with Express.

Document: Extensions

Assets can be modified externally with custom made NPM packages at the end of the finalization process. These are configurable only in Express settings and not through a RequestData object.

{
  "document": {
    "android": {
      "handler": "@pi-r/android",
      "extensions": [
        "@pi-r/android/extensions/app/manifest",
        "@pi-r/android/extensions/gradle/settings",
        "@pi-r/android/extensions/gradle/dependencies",
        "custom-android-extension"
      ]
    }
  }
}

// npm i custom-android-extension
module.exports = function(instance, documentDir) { // this = FileManager
    const mainSrcDir = path.join(this.baseDirectory, instance.mainParentDir, instance.mainSrcDir);
    /* Modify instance.assets */
};

NOTE: Asynchronous functions are supported.

Permissions

Copying files to a destination folder requires enabling "write" privileges. These folders can be restricted using glob patterns which are only configurable in settings or overidden using the CLI.

{
  "disk_read": false,
  "disk_write": true, // Inherited
  "unc_read": false,
  "unc_write": false,
  "node": {
    "modules": {
      "exclude": ["rollup", "terser"] // Packages that do not install correctly without a server restart
    }
  },
  "permission": {
    "disk_read": ["**"],
    "disk_write": ["/var/www/**", "/user/workspace/**"],
    "unc_read": "**",
    "unc_write": [/* none */], // Default is "**"
    "home_read": true, // boolean only
    "home_write": false,
    "process_exec": ["npm"] // Required for auto-install (node.modules.exclude)
  },
  "auth": {
    "algorithm": {
      "HS": {
        "enabled": true, // HS256 | HS384 | HS512
        "key": "secret123"
      },
      "PS": {
        "enabled": false,
        "cert": "/path/cert.pem" // PEM format
      }
    },
    "client": {
      "username": { // Any string
        "cipher": { // Optional
          "algorithm": "AES",
          "key": "key123"
        },
        "password": "password123", // When using hash or cipher copy password from JSON response
        "permission": {
          "host": {
            "inherit": true, // Inherits from "permission"
            "disk_write": ["**"]
          },
          "chrome": { // Module name
            "inherit": false, // Explicit "false" to disable
            "disk_write": ["/project/chrome/**"]
          }
        }
      }
    }
  },
  "document": {
    "chrome": {
      "permission": {
        "inherit": true,
        "disk_write": ["/var/www/**"]
      }
    }
  }
}

Directory: sqd.config

HTML page directories can contain a "sqd.config" file that can be used to store request data for multiple pages in either JSON or YAML format. Name resolution order goes by full path and then filename. Search parameters are also interpreted when the page is served.

<!-- http://localhost:3000/project/bundle.html -->

<script>
    squared.copyTo("/path/output", { config: true }); // Same as http://localhost:3000/project/sqd.config
    /* OR */
    squared.copyTo("/path/output", { config: { uri: true, key: "111-111-111" } });
</script>
// sqd.config

{
  "/project/bundle.html?id=1": {
    "ordinal": 1, // Used with config.inherit
    "useOriginalHtmlPage": true
    "elements": [{ "selector": "html", "type": "html" }]
  },
  "111-111-111": [{ "selector": "html", "type": "html" }], // key
  "/project/bundle.html": [{ "selector": "html", "type": "html" }],
  "bundle.html?id=1": [{ "selector": "html", "type": "html" }],
  "bundle.html": [{ "selector": "html", "type": "html" }],
  "**/*.html": [{ "selector": "html", "type": "html" }], // Glob patterns
  "**/*.html\\?id=1": [{ "selector": "html", "type": "html" }] // Escaping "?" is required (RegExp special characters)
}

Settings

squared.json (v3) is no longer backwards compatible with v1 and v2. Warnings will be given due to the use of unsafe migration routines from v2 to v3. Check the release notes (v3.0.0) for affected properties.

  1. squared.json
  2. squared.config.json (suffix)
  3. squared.settings.json (prefix)

NOTE: File extension ".cjs" can also be used which gives you full access to the NodeJS API.

// squared.cjs

module.exports = {
  document: {
    chrome: {
      handler: "@pi-r/chrome",
      settings: {
        transform: {
          js: {
            terser: {
              minify: async function (terser, value, options) {
                return await terser.minify(value, options.outputConfig).code; // npm i terser
              }
            }
          }
        }
      }
    }
  }
};

API Routes

v1.0.0

// NOTE: {required} [optional]

POST: "/api/v1/assets/archive?format={zip|tar|7z|gz}&filename=[no_ext]&to=[disk_uri]&append_to=[archive_uri]"

POST: "/api/v1/assets/copy?to={disk_uri}&empty=[0|1|2]" // Target directory (1=sub|2=base)

GET: "/api/v1/loader/data/json?key={id}&cache=[0|1]"
GET: "/api/v1/loader/data/json?key={id}&cache=[0|1]&mime=[json|yaml|toml]"

GET: "/api/v1/loader/data/blob?key={id}&cache=[0|1]"
GET: "/api/v1/loader/data/text?key={id}&cache=[0|1]"
GET: "/api/v1/loader/data/document?key={id}&cache=[0|1]"
GET: "/api/v1/loader/data/arraybuffer?key={id}&cache=[0|1]"

v1.1.0

POST: "/api/v1/websocket/observe?pathname={spec_URL}"

v1.2.0

GET: "/api/v1/auth/jwt/sign?alg={HS256|HS384|HS512|RS256|RS384|RS512|PS256|PS384|PS512|ES256|ES384|ES512}&username={string}&password={string}&aud=[string|string[]]&iss=[string|string[]]&sub=[string]&jti=[string]&nbf=[1y 1d 1h 1m]&expires=[1y 1d 1h 1m]" // env=development (v2.0)

GET: "/api/v1/auth/jwt/verify?token={string}"

v1.2.1

GET: "/api/v1/auth/jwt/sign?alg={string}&username={string}&password={string}&nonce=[0|1]" // v2.2
GET: "/api/v1/auth/jwt/sign?hash=[MD5|SHA1|SHA3|SHA224|SHA256|SHA384|SHA512|RIPEMD160]&passphrase=[string]" // passphrase (optional)
GET: "/api/v1/auth/jwt/sign?cipher=[AES|DES|TripleDES|Rabbit|RC4|RC4Drop]&key=[string]" // key (required)

v1.3.0

GET: "/api/v1/threads/count?as=[text]" // v2.7
GET: "/api/v1/threads/stat"
POST: "/api/v1/threads/kill?pid={uuid}" // Internal (squared)

POST: "/api/v1/admin/threads/stat" // JWT auth header (required)
POST: "/api/v1/admin/threads/kill?pid=[number]&all=[0|1]&as=[text]" // Multiple supported - ?pid=1&pid=2

v1.3.1

POST: "/api/v1/admin/auth/jwt/sign" // Same as GET params (auth.settings.admin.users)

LICENSE

BSD 3-Clause

Package Sidebar

Install

npm i squared-express

Weekly Downloads

4

Version

3.1.6

License

BSD 3-Clause

Unpacked Size

323 kB

Total Files

16

Last publish

Collaborators

  • anpham6