gina 0.3.13 → 0.3.14
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/CHANGELOG.md +15 -0
- package/README.md +11 -1
- package/framework/v0.3.14/VERSION +1 -0
- package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.js +52 -2
- package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.min.js +8 -7
- package/framework/v0.3.14/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
- package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
- package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.js +33 -3
- package/framework/{v0.3.13 → v0.3.14}/core/gna.js +19 -0
- package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/validator/src/main.js +52 -2
- package/framework/{v0.3.13 → v0.3.14}/core/router.js +12 -5
- package/framework/{v0.3.13 → v0.3.14}/core/server.isaac.js +74 -0
- package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/list.js +23 -9
- package/framework/{v0.3.13 → v0.3.14}/lib/cmd/helper.js +36 -21
- package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/remove.js +11 -0
- package/framework/{v0.3.13 → v0.3.14}/package.json +2 -2
- package/gna.js +4 -4
- package/llms.txt +17 -9
- package/package.json +3 -3
- package/schema/app.json +13 -0
- package/framework/v0.3.13/VERSION +0 -1
- package/framework/v0.3.13/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/AUTHORS +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/html/nolayout.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/html/static.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/android-chrome-192x192.png +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/android-chrome-512x512.png +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/apple-touch-icon.png +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/favicon-16x16.png +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/favicon-32x32.png +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/img/favicon.ico +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/inspector/index.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/config.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/ai/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/ai/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/connector.v2.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/connector.v3.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/connector.v4.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/n1ql.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/session-store.v2.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/session-store.v3.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/couchbase/lib/session-store.v4.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mongodb/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mongodb/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mongodb/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mysql/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/mysql/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/postgresql/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/postgresql/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/redis/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/redis/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/scylladb/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/scylladb/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/scylladb/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/sql-parser.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/sqlite/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/sqlite/lib/connector.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/connectors/sqlite/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/content.encoding +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.framework.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.render-json.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.render-nunjucks.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.render-stream.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.render-swig.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/controller.render-v1.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/controller/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/lib/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/lib/types/multipart.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/lib/types/urlencoded.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/lib/utils.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/busboy-1.6.0/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/streamsearch-1.1.0/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/streamsearch-1.1.0/lib/sbmh.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/deps/streamsearch-1.1.0/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/dev/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/dev/lib/class.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/dev/lib/factory.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/dev/lib/tools.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/currency.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/dist/language/en.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/dist/language/fr.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/dist/region/en.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/dist/region/fr.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/locales/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/mime.types +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/model/entity.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/model/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/model/template/entityFactory.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/model/template/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/csrf/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/csrf/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/csrf/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/session/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/session/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/session/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/storage/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/storage/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/storage/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/storage/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/validator/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/validator/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/validator/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/plugins/lib/validator/src/form-validator.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/server.express.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/server.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/status.codes +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/_gitignore +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/app.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/connectors.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/routing.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/settings.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/templates.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/config/watchers.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle/locales/en.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/css/default.css +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/css/home.css +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/readme.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_public/sw.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/command/gina.bat.tpl +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/command/gina.tpl +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/env.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/manifest.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/settings.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/statics.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/conf/templates.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/client/json/401.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/client/json/403.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/client/json/404.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/server/html/50x.html +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/server/json/500.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/error/server/json/503.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/core/template/extensions/logger/config.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/console.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/context.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/data/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/data/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/data/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/data/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/dateFormat.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/json/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/json/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/json/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/json/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/path.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/plugins/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/plugins/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/plugins/src/api-error.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/plugins/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/prototypes.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/task.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/helpers/text.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/archiver/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/archiver/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/archiver/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/archiver/src/dep/jszip.min.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/archiver/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/async/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/async/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cache/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cache/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cache/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cache/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/aliases.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/arguments.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/build.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/copy.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/cp.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/mcp-start.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/mcp.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/oas.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/openapi.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/remove.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/rename.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/restart.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/rm.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/start.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/status.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/bundle/stop.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/cache/stats.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/arguments.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/migrate.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/remove.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/connector/rm.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/get.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/link-dev.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/remove.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/rm.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/set.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/unset.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/env/use.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/arguments.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/build.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/dot.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/get.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/init.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/link-node-modules.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/link.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/msg.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/open.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/restart.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/set.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/start.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/status.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/stop.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/tail.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/update.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/framework/version.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/gina-dev.1.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/gina-framework.1.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/gina.1.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/arguments.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/export.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/import.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/i18n/scan.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/inspector/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/inspector/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/inspector/open.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/minion/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/minion/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/msg.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/inc/scan.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/reset.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/port/set.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/arguments.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/build.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/import.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/move.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/rename.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/restart.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/rm.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/start.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/status.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/project/stop.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/protocol/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/protocol/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/protocol/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/protocol/set.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/link-local.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/link-production.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/remove.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/rm.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/scope/use.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/service/help.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/service/help.txt +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/service/list.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cmd/view/add.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/collection/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/collection/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/collection/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/collection/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/config.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/connector-registry/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/connector-registry/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cron/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cron/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/cron/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/domain/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/domain/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/domain/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/domain/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/generator/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/i18n/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/i18n/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inherits/LICENSE +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inherits/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inherits/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inherits/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inspector-redact/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/inspector-redact/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/default/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/file/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/mq/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/mq/listener.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/containers/mq/speaker.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/helper.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/logger/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/math/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-dispatch/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-dispatch/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-http/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-http/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-server/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/mcp-server/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/merge/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/merge/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/merge/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/metrics/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/metrics/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/model.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/nunjucks-filters/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/nunjucks-filters/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/nunjucks-filters/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/nunjucks-resolver/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/nunjucks-resolver/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/proc.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing/build.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing/src/radix.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing-introspect/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/routing-introspect/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/secrets/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/secrets/src/backends/env.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/secrets/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/session-store.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/shell.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/state.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/swig-filters/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/swig-filters/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/swig-filters/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/swig-resolver/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/swig-resolver/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/url/README.md +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/url/index.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/url/routing.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/uuid/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/uuid/src/main.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/validator.js +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/watcher/package.json +0 -0
- /package/framework/{v0.3.13 → v0.3.14}/lib/watcher/src/main.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|
|
6
6
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## 0.3.14 - 2026-05-16
|
|
10
|
+
### Changed
|
|
11
|
+
* Bumped the @rhinostone/swig dependency floor from ^2.3.0 to ^2.4.0 in framework/v*/package.json and root package.json. Version 2.4.0 adds ternary (a ? b : c) and Elvis (a ?: b) operator support in template expressions — usable in {{ }} output and in tag arguments such as {% if %}, {% set %}, {% for %} — plus two CLI/build fixes (mocha invocation in make coverage; EEXIST guard in swig compile -o). No template-engine API change at the surface gina calls; swigResolver DEFAULT_MIN stays at 2.0.0.
|
|
12
|
+
### Fixed
|
|
13
|
+
* FormValidator::validateFormById now fails loud with an actionable error when the resolved id is not an HTMLFormElement — for example when a sibling <p id="X"> or <div id="X"> shares the id with a later-loaded <form id="X"> from a popin or AJAX fragment — instead of crashing later inside bindForm with a cryptic TypeError on the undefined .elements.length access. The error names the offending tag (e.g. `parent` resolves to <P>, not a FORM) and suggests renaming so the underlying id collision is visible from the console message. The guard mirrors the existing instanceof HTMLFormElement discriminator already used in resetErrorsDisplay elsewhere in the same file.
|
|
14
|
+
* FormValidator::getFormById now fails loud with the same actionable error as validateFormById when the resolved id is not an HTMLFormElement — for example when a sibling <p id="X"> or <div id="X"> shares the id with a later-loaded <form id="X"> from a popin or AJAX fragment — instead of letting initForm register the non-FORM element in instance.$forms (polluting subsequent lookups) and crashing later inside bindForm with a cryptic TypeError on the undefined .elements.length access. The error names the offending tag (e.g. `parent` resolves to <P>, not a FORM) and suggests renaming so the underlying id collision is visible from the console message. Sister guard to the validateFormById fix in the previous commit; same shape and same root cause, different code path.
|
|
15
|
+
* gina bundle:list — two argv parsing bugs in lib/cmd/bundle/list.js. (Bug A) Bare `gina bundle:list` no longer emits the spurious red `[error][gina] [ null ] is not a valid project name` stderr line before falling through to listAll(); CmdHelper initialises projectName to null (per lib/cmd/helper.js:77) but the previous post-loop dispatch only handled the undefined case. (Bug B) `gina bundle:list --all --format=json` now correctly prints JSON instead of text — the previous in-loop short-circuit on `!self.projectName` returned to listAll() before the `--format=json` token at a later argv position could set self.format. Restructure separates parsing from dispatch: the argv walk now only sets self.format + an allFlag, and dispatch runs once after the full scan completes (`if (allFlag || self.projectName == null) listAll() else if (isDefined(...)) listProjectOnly()`). Loose `== null` matches both null and undefined cleanly. The `--all` regex was extended from `/^--all=/` to `/^--all(=|$)/` so bare `--all` is matched explicitly instead of falling through to the projectName short-circuit. No functional change for valid argv shapes (`gina bundle:list @<project>` and `gina bundle:list --format=json --all` both work identically before and after).
|
|
16
|
+
* Framework VERSION file no longer drifts by 1 version after each post-publish bumpVersion cycle — post_publish.js now writes framework/v{new}/VERSION alongside the renamed framework directory's package.json, sibling to the existing gitignored-file rewrite. Without this, VERSION moved with the renameSync but kept the prior publish's content, so framework/v{new}/VERSION reported the previous version until the next npm publish fired prepare_version.js. No runtime impact today (no readers); fixes the visible drift for anyone inspecting the file directly.
|
|
17
|
+
* #B18 — `core/router.js` no longer poisons `require.cache[controller/index.js]` after eviction. The two sites — `refreshCoreDependencies()` (~L116-127) and the controller-require path (~L617-627) — previously did `delete require.cache[path]` then `require.cache[path] = require(path)`, which assigns the exports OBJECT (not a `Module` instance) into the slot. Latent bug because router.js was the slot's only consumer and read it back self-consistently, but the antipattern would have surfaced as `undefined`-exports crashes the moment any other module did a plain `require(controller/index.js)`. Mirror of the `refreshCore()` fix already shipped in commit `add6655e` for `core/server.isaac.js`. Both sites now use the return value of `require(...)` directly; the preceding `delete require.cache[...]` already evicts the slot so the next `require()` builds a fresh `Module` correctly. No functional change observable today — closes a documented latent bug from llms.txt #104.
|
|
18
|
+
* #B16 — `gina project:rm @<project> --force` no longer ENOENTs when the project state is partially broken (missing `manifest.json` and/or missing project directory). The whole point of `--force` on a rm command is to tolerate partial breakage, but the framework's shared `CmdHelper.loadAssets()` was reading `manifest.json` unconditionally before the handler even started, and the handler itself was erroring out when the project directory was gone. Two coordinated fixes: (a) `lib/cmd/helper.js` `loadAssets()` now stubs `cmd.projectData` with a minimal `{project, version, bundles}` shape when the task is `:remove` and `--force` is set, skipping both the recreate-from-template attempt (which itself fails when the dir is gone) and the downstream `requireJSON` read; (b) `lib/cmd/project/remove.js` `init()` now warns and dispatches directly to `end(true)` when the project folder is missing AND `--force` is set, bypassing the prompt and folder rmSync to land registry-only removal (ports cleanup + `~/.gina/projects.json` delete). Without `--force`, both sites preserve the original error + exit behaviour so typos and wrong-machine invocations still surface immediately. The registry-missing check (`projects[projectName] not found in ~/.gina/projects.json`) is unchanged — `--force` can't help when there's no record to remove.
|
|
19
|
+
### Security
|
|
20
|
+
* Controller::throwError now strips the `stack` field from JSON error responses outside the local scope (NODE_SCOPE_IS_LOCAL=true). Local scope keeps the stack on the wire so the dev toolbar's data-xhr panel can render server-side stack frames; beta, testing, production, and any unset scope always strip — preventing server-side internals (file paths, library versions, internal stack frames) from leaking to API clients. Fail-closed: when the scope flag is missing or false for any reason, the wire stays clean. The toolbar is the only verified consumer (events.js:394 `JSON.parse(xhr.responseText)` → `ginaToolbar.update("data-xhr", XHRData)`); it is already gated on its own load path and does not load outside local scope. Out of scope and tracked separately: the HTML error-page branch (generic `<pre class="stack">` element at controller.js:5292-5333, served only when no custom error template is configured) still leaks in non-local scopes; and consumer-side leaks where `err.stack` is passed directly as the msg argument and surfaces in the `error` field instead of the gated `stack` field — those require per-call-site sanitization on the consumer side.
|
|
21
|
+
* Controller::throwError now also gates the fallback HTML error page on local scope — the `<pre class="stack">` block under the L5294+ msgString construction is rendered only when NODE_SCOPE_IS_LOCAL=true, so server-side stack frames (file paths, library versions, internal frames) don't leak via the generic HTML error page in non-local scopes. Same fail-closed shape as the JSON wire gate landed in the previous Security entry; covers both render sites in the fallback path (msg-shape and generic-shape branches). Title / error / message <pre> blocks continue to render in every scope. Custom error templates dispatched via renderCustomError stay consumer-owned — what the template chooses to render from req.params.errorObject (e.g. {% if data.stack %}{{ data.stack }}{% endif %} in a consumer view) is the consumer's call, not the framework's. Closes the cross-shape part of the throwError stack-leak story; consumer-side leaks where `err.stack` is passed as the msg argument and surfaces in the `error` string still need per-call-site sanitization on the consumer side.
|
|
22
|
+
* Built-in /_gina/info and /_gina/cache/stats endpoints are now IP-allowlisted per bundle (#S7). They expose process state (memory, uptime, version, HTTP/2 session counters, cache contents) — admin-grade information that should not reach public callers. The new `admin.allowFrom` block in `app.json` defines the allowlist; defaults to loopback only (`["127.0.0.1", "::1"]`) when omitted. Empty array `[]` is explicit deny-everyone. The IP is read from `req.socket.remoteAddress` only — `X-Forwarded-For` is NOT trusted because reverse proxies could spoof it. `::ffff:IPv4` (IPv6-mapped IPv4) is normalised to `IPv4` so listing `127.0.0.1` matches both forms. Denied requests receive 403 with `{ error: "forbidden", message: "/_gina/<path>: client IP not in app.json admin.allowFrom" }`. Mirrors the /_gina/metrics gate convention (#OBS1) but is configured on a separate axis — bundles that disable metrics still get the admin lockdown by default. /_gina/health/check is unaffected (k8s liveness probes need it unrestricted). To allow scraping from an internal monitor on a sister host, add the monitor IP to `app.json admin.allowFrom`.
|
|
23
|
+
|
|
9
24
|
## 0.3.13 - 2026-05-14
|
|
10
25
|
### Added
|
|
11
26
|
* Released `0.3.13` — cumulative stable of the `0.3.13-alpha` cycle. Alpha-cycle headline tracks (detailed under `0.3.13-alpha.3` in the changelog): **`${secret:KEY}` config-placeholder resolver** (#SECRETS1) — `lib/secrets` substitutes `${secret:KEY}` placeholders embedded in bundle JSON configs (`settings.json`, `app.json`, `connectors.json`, `mcp.json`) from `process.env[KEY]` at config-load time, fail-closed on an unset/empty var; ships with a `settings.csrf.secret` slot for `gina.plugins.Csrf()` and `mcp.json` placeholder support for `gina bundle:mcp-start`. **Eval-safety campaign completed** (#M20–#M22) — validator nested-key rule construction refactored off `eval` (#M20); the four HTML event-callback `eval` sites and the conditional-binding evaluator fallback dropped (#M21a, #M21b); the Pattern B user-defined-validator `eval` documented as load-bearing public API with an invariant test (#M21c); the logger ↔ merge circular-require restructured so the three `eval(fs.readFileSync(...))` fallbacks are removed (#M22) — Phase 3c complete, one load-bearing framework `eval` remains by design. Plus the #M1 async-race retrofit follow-up (render-nunjucks terminal-exit closure nulling) and a `requireJSON` line-comment / URL-collision fix. The entries below are the post-alpha-cycle changes that complete the release.
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Node.js MVC framework with built-in HTTP/2, multi-bundle architecture, and scope
|
|
|
22
22
|
| ORM / entities | EventEmitter-based entity system; SQL files auto-wired to entity methods |
|
|
23
23
|
| Connectors | Couchbase, MongoDB, ScyllaDB / Cassandra, MySQL, PostgreSQL, Redis, SQLite, AI (LLM) — loaded from project `node_modules` |
|
|
24
24
|
| AI connector | Any LLM provider via named protocol (`anthropic://`, `openai://`, `ollama://`, …) |
|
|
25
|
-
| Template engine | [`@rhinostone/swig`](https://github.com/gina-io/swig) 2.
|
|
25
|
+
| Template engine | [`@rhinostone/swig`](https://github.com/gina-io/swig) 2.4.0 — maintained fork with CVE-2023-25345 patched; streaming SSE/chunked via `renderStream()`. Nunjucks supported as opt-in via `render.engine = "nunjucks"` |
|
|
26
26
|
| Internationalisation | Per-bundle JSON catalogs, `t()` helper, swig + nunjucks `t` filter, CLDR plurals, ICU MessageFormat opt-in via `t.icu()` |
|
|
27
27
|
| Observability | Built-in `/_gina/metrics` Prometheus endpoint (opt-in, IP-allowlisted) — Node.js process metrics + HTTP counter / duration histogram with cardinality-safe route labels |
|
|
28
28
|
| Hot reload | WatcherService evicts `require.cache` only on file change — zero per-request overhead in dev |
|
|
@@ -39,6 +39,16 @@ gina bundle:start api @myproject
|
|
|
39
39
|
open https://localhost:3100
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## What's in 0.3.14
|
|
43
|
+
|
|
44
|
+
- **Server-side stack-frame leak prevention on both error wires** — `Controller::throwError` now gates the `stack` field in JSON error responses and the `<pre class="stack">` block in the fallback HTML error page on local scope. Beta, testing, production, and any unset scope strip — preventing file paths, library versions, and internal stack frames from reaching API clients and page viewers. Local scope (`NODE_SCOPE_IS_LOCAL=true`) preserves the stack on both wires so the dev toolbar's `data-xhr` panel and the fallback HTML page keep showing the trace inline. Fail-closed shape (gate is `!_isLocalScope`, not `_isProdScope`) so a missing scope var on a fresh deployment cannot reintroduce the leak. Custom error templates dispatched via `renderCustomError` stay consumer-owned — what a consumer view renders from `req.params.errorObject` is unchanged. Consumer-side leak where `err.stack` is passed as the `msg` argument and surfaces in the `error` STRING is NOT covered by the framework gate — those callsites need per-callsite sanitization (`new Error('...').message` instead of `.stack`).
|
|
45
|
+
- **Admin-grade `/_gina/*` endpoints IP-allowlisted** (#S7) — `/_gina/info` (process memory / uptime / version / HTTP/2 session counters) and `/_gina/cache/stats` (full cache contents) now require an IP allowlist configured via a new `admin.allowFrom` block in `app.json`. Defaults to loopback only (`["127.0.0.1", "::1"]`); empty array `[]` is explicit deny-everyone. Client IP is read from `req.socket.remoteAddress` only — `X-Forwarded-For` is NOT trusted (reverse proxies could spoof it). `::ffff:IPv4` is normalised so listing `127.0.0.1` matches both forms. `/_gina/health/check` is intentionally unrestricted (k8s liveness probes need it open). Mirrors the `/_gina/metrics` (#OBS1) gate convention but on a separate axis — bundles that disable metrics still get the admin lockdown. **Action required:** bundles that scrape `/_gina/info` or `/_gina/cache/stats` from a non-loopback host (internal monitor, sister K8s pod, etc.) must add the source IP to `app.json admin.allowFrom`. Localhost-only access (typical dev workflow) needs no change.
|
|
46
|
+
- **`gina project:rm --force` tolerates partial-breakage states** (#B16) — the whole point of `--force` on a rm command is to honour broken state (manually-deleted directory, failed scaffold mid-disk-full, stuck `projects.json` row with no on-disk artefacts). Pre-fix the framework errored on missing `manifest.json` and missing project directory even with `--force` set. Both now warn and fall back to registry-only removal (`~/.gina/projects.json` + state-store row). Without `--force` both still error so typos and wrong-machine invocations surface immediately. The registry-missing check is unchanged — `--force` cannot help when there is no record to remove.
|
|
47
|
+
- **`bundle:list` argv parsing cleanup** (#B15) — two pre-existing surface bugs. Bare `gina bundle:list` no longer emits a spurious red `[error][gina] [ null ] is not a valid project name` stderr line before falling through to the all-projects view. `gina bundle:list --all --format=json` now correctly prints JSON instead of text — the previous in-loop short-circuit dispatched to `listAll()` before the `--format=json` token at a later argv position could set the format. No functional change for valid argv shapes.
|
|
48
|
+
- **FormValidator HTMLFormElement guards** — `FormValidator::validateFormById` and `FormValidator::getFormById` now fail loud with an actionable error when the resolved id is not an `HTMLFormElement` (for example when a sibling `<p id="X">` or `<div id="X">` shares the id with a later-loaded `<form id="X">` from a popin or AJAX fragment), instead of crashing later inside `bindForm` with a cryptic `TypeError` on `.elements.length`. The error names the offending tag and suggests renaming so the id collision is visible from the console message.
|
|
49
|
+
- **`@rhinostone/swig` floor bumped to `^2.4.0`** — `2.4.0` adds ternary (`a ? b : c`) and Elvis (`a ?: b`) operator support in template expressions, plus two CLI/build fixes (`mocha` invocation in `make coverage`; `EEXIST` guard in `swig compile -o`). No template-engine API change at the surface gina calls. `swigResolver DEFAULT_MIN` stays at `2.0.0`.
|
|
50
|
+
- **Internal housekeeping** — `core/router.js` hot-reload no longer poisons the `require.cache` slot for `controller/index.js` (#B18, mirror of the `refreshCore()` fix shipped in `add6655e`). `script/post_publish.js bumpVersion` now refreshes the `framework/v*/VERSION` file content alongside the `renameSync` of the gitignored sibling, closing a drift family that previously needed manual repair after each alpha bump.
|
|
51
|
+
|
|
42
52
|
## What's in 0.3.13
|
|
43
53
|
|
|
44
54
|
- **`${secret:KEY}` placeholder substitution** (#SECRETS1) — bundle JSON configs (`settings.json`, `app.json`, `connectors.json`, `mcp.json`, …) can embed `${secret:KEY}` placeholders that the framework resolves from `process.env[KEY]` at config-load time, before the merged config reaches `getConfig()` or any plugin factory. Anchored to whole-string values; fails closed on an unset/empty var. `gina.plugins.Csrf()` reads `settings.csrf.secret` through it, and `gina bundle:mcp-start` resolves `mcp.json` placeholders such as `server.authToken`. Pluggable-backend interface reserved for a future iteration; only the `process.env` backend ships now.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.14
|
|
@@ -9823,6 +9823,18 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
9823
9823
|
}
|
|
9824
9824
|
|
|
9825
9825
|
|
|
9826
|
+
/**
|
|
9827
|
+
* getFormById
|
|
9828
|
+
*
|
|
9829
|
+
* @param {string} formId
|
|
9830
|
+
*
|
|
9831
|
+
* @returns {object} $form
|
|
9832
|
+
*
|
|
9833
|
+
* @throws {Error} When `formId` resolves via document.getElementById to a non-FORM
|
|
9834
|
+
* element. Same shape and root cause as validateFormById's @throws —
|
|
9835
|
+
* a sibling <p id="X"> / <div id="X"> shares the id with a later-
|
|
9836
|
+
* loaded <form id="X"> (popin / AJAX fragment).
|
|
9837
|
+
* */
|
|
9826
9838
|
var getFormById = function(formId) {
|
|
9827
9839
|
var $form = null, _id = formId;
|
|
9828
9840
|
|
|
@@ -9836,8 +9848,25 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
9836
9848
|
_id = _id.replace(/\#/, '');
|
|
9837
9849
|
|
|
9838
9850
|
// in case form is created on the fly and is not yet registered
|
|
9839
|
-
|
|
9840
|
-
|
|
9851
|
+
var $candidate = document.getElementById(_id);
|
|
9852
|
+
if ($candidate != null && typeof (instance['$forms'][_id]) == 'undefined') {
|
|
9853
|
+
|
|
9854
|
+
// Same fail-loud guard as validateFormById's else branch —
|
|
9855
|
+
// document.getElementById returns the first matching element regardless
|
|
9856
|
+
// of tag, so a sibling <p id="X"> / <div id="X"> / etc. wins over a
|
|
9857
|
+
// later-loaded <form id="X">. Surface the collision instead of letting
|
|
9858
|
+
// initForm register the non-FORM element in instance.$forms (polluting
|
|
9859
|
+
// subsequent lookups) and crash later inside bindForm with
|
|
9860
|
+
// `Cannot read properties of undefined (reading 'length')`.
|
|
9861
|
+
if ( !($candidate instanceof HTMLFormElement) ) {
|
|
9862
|
+
throw new Error(
|
|
9863
|
+
'[ FormValidator::getFormById(formId) ] `' + _id + '` resolves to <'
|
|
9864
|
+
+ $candidate.tagName + '>, not a FORM. A non-FORM element shares the same id as '
|
|
9865
|
+
+ 'the target form — rename one of them so the id is unique.'
|
|
9866
|
+
);
|
|
9867
|
+
}
|
|
9868
|
+
|
|
9869
|
+
initForm( $candidate );
|
|
9841
9870
|
}
|
|
9842
9871
|
|
|
9843
9872
|
if ( typeof(instance.$forms[_id]) != 'undefined' ) {
|
|
@@ -9909,6 +9938,12 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
9909
9938
|
* @param {object} [customRule]
|
|
9910
9939
|
*
|
|
9911
9940
|
* @returns {object} $form
|
|
9941
|
+
*
|
|
9942
|
+
* @throws {Error} When `formId` resolves via document.getElementById to a non-FORM
|
|
9943
|
+
* element (e.g. a sibling <p id="X"> / <div id="X"> sharing the id
|
|
9944
|
+
* with a later-loaded <form id="X">). Surfaces the collision instead
|
|
9945
|
+
* of crashing later inside bindForm with a cryptic
|
|
9946
|
+
* `Cannot read properties of undefined (reading 'length')`.
|
|
9912
9947
|
* */
|
|
9913
9948
|
var validateFormById = function(formId, customRule) {
|
|
9914
9949
|
var $form = null
|
|
@@ -9952,6 +9987,21 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
9952
9987
|
$form = this.$forms[_id] = instance['$forms'][_id];
|
|
9953
9988
|
} else { // binding a form out of context (outside of the main instance)
|
|
9954
9989
|
$target = document.getElementById(_id);
|
|
9990
|
+
|
|
9991
|
+
// validateFormById was designed for FORM elements. getElementById
|
|
9992
|
+
// returns the first matching element regardless of tag, so a sibling
|
|
9993
|
+
// <p id="X"> / <div id="X"> / etc. wins over a later-loaded
|
|
9994
|
+
// <form id="X">. Surface the collision instead of crashing later
|
|
9995
|
+
// inside bindForm with a cryptic
|
|
9996
|
+
// `Cannot read properties of undefined (reading 'length')`.
|
|
9997
|
+
if ( $target && !($target instanceof HTMLFormElement) ) {
|
|
9998
|
+
throw new Error(
|
|
9999
|
+
'[ FormValidator::validateFormById(formId) ] `' + _id + '` resolves to <'
|
|
10000
|
+
+ $target.tagName + '>, not a FORM. A non-FORM element shares the same id as '
|
|
10001
|
+
+ 'the target form — rename one of them so the id is unique.'
|
|
10002
|
+
);
|
|
10003
|
+
}
|
|
10004
|
+
|
|
9955
10005
|
$validator.id = _id;
|
|
9956
10006
|
$validator.target = $target;
|
|
9957
10007
|
|
|
@@ -277,13 +277,14 @@ delete require.cache[require.resolve('../../../../../helpers/data')];require('..
|
|
|
277
277
|
document:null,errors:{},initialized:!1,isReady:!1,rules:{},$forms:{},getFormById:null,validateFormById:null,setOptions:null,resetErrorsDisplay:null,resetFields:null},S={id:null,plugin:this.plugin,on:C?on:null,eventData:{},target:C?document:null,cachedErrors:{},lastFocused:C?[]:null,binded:!1,unbinded:!1,withUserBindings:!1,rules:{},setOptions:null,send:null,isValidating:null,isSubmitting:null,submit:null,destroy:null,resetErrorsDisplay:null,resetFields:null},ka={},aa={},v=null,u={url:'',method:'GET',
|
|
278
278
|
isSynchrone:!1,withCredentials:!1,withRateLimit:!0,headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8','X-Requested-With':'XMLHttpRequest'}},V=function(){if(typeof document==='undefined'||!document||typeof document.cookie!=='string')return null;var a=document.cookie||'';if(!a)return null;a=a.split(';');for(var b=0,e=a.length;b<e;++b){for(var k=a[b];k.charAt(0)===' '||k.charAt(0)==='\t';)k=k.slice(1);var z=k.indexOf('=');if(!(z<0)&&k.slice(0,z)==='gina-csrf-token'){a=k.slice(z+
|
|
279
279
|
1);try{return decodeURIComponent(a)}catch(y){return a}}}return null},h=function(a){if(typeof a!=='string'||!a)return!1;a=a.toUpperCase();return a!=='GET'&&a!=='HEAD'&&a!=='OPTIONS'},g=function(a,b,e){e=typeof e!='undefined'?{id:e}:null;var k={},z;for(z in b)k[z]=b[z];if(typeof a!='undefined'&&a.count()>0){try{wa(a,''),va(a)}catch(y){throw y;}return T(e,k,null,c.rules)}return new n(k)},q=function(a){u=a=D(a,u);return this},F=function(a){var b=null;if(!c.$forms)throw Error('`$forms` collection not found');
|
|
280
|
-
if(typeof a=='undefined')throw Error('[ FormValidator::getFormById(formId) ] `formId` is missing');a=a.replace(/#/,'');
|
|
281
|
-
S);if(k=e.getAttribute('data-gina-form-rule')){k=k.replace(/\-|\//g,'.');if(typeof z[k]=='undefined')throw Error('['+e.id+'] no rule found with key: `null`');k=z[k]}typeof e.id=='string'&&typeof z[e.id.replace(/\-/g,'.')]!='undefined'?$target=c.$forms[e.id].target:typeof e.id=='object'?(delete c.$forms[e.id],
|
|
282
|
-
Ya($target)}}typeof c.$forms[a]!='undefined'&&(c.$forms[a].withUserBindings=!0,b=typeof this.$forms!='undefined'&&typeof this.$forms[a]=='undefined'?this.$forms[a]=c.$forms[a]:c.$forms[a]);if(!b)throw Error('Validator::getFormById(...) exception: could not retrieve form `'+a+'`');b.binded||(Ya(b.target),b=c.$forms[a]);
|
|
283
|
-
(e.errors=c.$forms[a].errors),window.ginaToolbar.update('forms',e));return b},N=function(){var a=!1,b=null;gina.hasPopinHandler&&gina.popinIsBinded&&(b=gina.popin.getActivePopin());b&&b.isOpen&&(a=!0);return a},r=function(a,b){var e=typeof(ka.count()>0)?ka:c.rules;if(!c.$forms)throw Error('`$forms` collection not found');
|
|
284
|
-
if(typeof a=='string')a=a.replace(/#/,'');else{if(typeof a!='object'||Array.isArray(a))throw Error('[ FormValidator::validateFormById(formId[, customRule]) ] `formId` should be a `string`');var k=a.form;a=k.getAttribute('id')||'form.'+
|
|
285
|
-
k+'` is a duplicate form ID. If not fixed, this could lead to an undesirable behaviour.\n Check inside your popin content'):console.warn('Validator::bindForm($target, customRule): `'+k+'` is a duplicate form ID. If not fixed, this could lead to an undesirable behaviour.'),
|
|
286
|
-
|
|
280
|
+
if(typeof a=='undefined')throw Error('[ FormValidator::getFormById(formId) ] `formId` is missing');a=a.replace(/#/,'');var e=document.getElementById(a);if(e!=null&&typeof c.$forms[a]=='undefined'){if(!(e instanceof HTMLFormElement))throw Error('[ FormValidator::getFormById(formId) ] `'+a+'` resolves to <'+e.tagName+'>, not a FORM. A non-FORM element shares the same id as the target form \u2014 rename one of them so the id is unique.');var k,z=typeof(ka.count()>0)?ka:c.rules;e.getAttribute?(id=e.getAttribute('id')||
|
|
281
|
+
'form.'+U(),id!==e.getAttribute('id')&&e.setAttribute('id',id)):(id='form.'+U(),e.setAttribute('id',id));e.id=S.id=id;if(typeof e.id!='undefined'&&e.id!='null'&&e.id!=''){S.target=e;c.$forms[e.id]=D({},S);if(k=e.getAttribute('data-gina-form-rule')){k=k.replace(/\-|\//g,'.');if(typeof z[k]=='undefined')throw Error('['+e.id+'] no rule found with key: `null`');k=z[k]}typeof e.id=='string'&&typeof z[e.id.replace(/\-/g,'.')]!='undefined'?$target=c.$forms[e.id].target:typeof e.id=='object'?(delete c.$forms[e.id],
|
|
282
|
+
z=e.attributes.getNamedItem('id').nodeValue||'form.'+U(),e.setAttribute('id',z),e.id=z,S.target=e,c.$forms[z]=D({},S),$target=c.$forms[z].target):$target=c.$forms[e.id].target;k?Ya($target,k):Ya($target)}}typeof c.$forms[a]!='undefined'&&(c.$forms[a].withUserBindings=!0,b=typeof this.$forms!='undefined'&&typeof this.$forms[a]=='undefined'?this.$forms[a]=c.$forms[a]:c.$forms[a]);if(!b)throw Error('Validator::getFormById(...) exception: could not retrieve form `'+a+'`');b.binded||(Ya(b.target),b=c.$forms[a]);
|
|
283
|
+
A&&C&&typeof window.ginaToolbar!='undefined'&&window.ginaToolbar&&(gina.forms.errors||(gina.forms.errors={}),e={id:a,rules:c.$forms[a].rules},typeof c.$forms[a].errors!='undefined'&&(e.errors=c.$forms[a].errors),window.ginaToolbar.update('forms',e));return b},N=function(){var a=!1,b=null;gina.hasPopinHandler&&gina.popinIsBinded&&(b=gina.popin.getActivePopin());b&&b.isOpen&&(a=!0);return a},r=function(a,b){var e=typeof(ka.count()>0)?ka:c.rules;if(!c.$forms)throw Error('`$forms` collection not found');
|
|
284
|
+
if(typeof a!='undefined'&&typeof c.$forms[a]!='undefined')return c.$forms[a];if(typeof a=='undefined')if(typeof this.id!='undefined'&&this.id!=''&&this.id!=null)a=this.id;else throw Error('[ FormValidator::validateFormById(formId, customRule) ] `formId` is missing');if(typeof a=='string')a=a.replace(/#/,'');else{if(typeof a!='object'||Array.isArray(a))throw Error('[ FormValidator::validateFormById(formId[, customRule]) ] `formId` should be a `string`');var k=a.form;a=k.getAttribute('id')||'form.'+
|
|
285
|
+
U();k.setAttribute('id',a)}var z=document.getElementsByTagName('form');for(var y={},f=0,O=z.length;f<O;++f)k=z[f].getAttribute('id')||null,typeof y[k]=='undefined'?y[k]=!0:typeof c.$forms[k]=='undefined'||c.$forms[k].warned||(gina.popinIsBinded?console.warn('Popin/Validator::bindForm($target, customRule): `'+k+'` is a duplicate form ID. If not fixed, this could lead to an undesirable behaviour.\n Check inside your popin content'):console.warn('Validator::bindForm($target, customRule): `'+k+'` is a duplicate form ID. If not fixed, this could lead to an undesirable behaviour.'),
|
|
286
|
+
c.$forms[k].warned=!0);if(typeof this.$forms!='undefined'&&typeof c.$forms[a]!='undefined')z=this.$forms[a]=c.$forms[a];else{if((k=document.getElementById(a))&&!(k instanceof HTMLFormElement))throw Error('[ FormValidator::validateFormById(formId) ] `'+a+'` resolves to <'+k.tagName+'>, not a FORM. A non-FORM element shares the same id as the target form \u2014 rename one of them so the id is unique.');S.id=a;S.target=k;z=this.$forms[a]=c.$forms[a]=D({},S);if(typeof b=='undefined')if(y=a.replace(/\-/g,
|
|
287
|
+
'.'),typeof e!='undefined')z.rule=Ja(y);else{if(typeof z.target!='undefined'&&z.target!==null&&z.target.getAttribute('data-gina-form-rule'))if(y=z.target.getAttribute('data-gina-form-rule').replace(/\-|\//g,'.'),typeof e!='undefined')z.rule=Ja(y);else throw Error('[ FormValidator::validateFormById(formId) ] using `data-gina-form-rule` on form `'+z.target+'`: no matching rule found');}else if(y=b.replace(/\-|\//g,'.'),typeof e!='undefined')z.rule=Ja(y);else throw Error('[ FormValidator::validateFormById(formId, customRule) ] `'+
|
|
287
288
|
b+'` is not a valid rule');k&&typeof this.isPopinContext!='undefined'&&/true/i.test(this.isPopinContext)&&(k.isPopinContext=this.isPopinContext);k&&!z.binded&&Ya(k,y)}if(!z)throw Error('[ FormValidator::validateFormById(formId, customRule) ] `'+a+'` not found');return z||null},x=function(a){var b=a.form.id||a.form.getAttribute('id'),e=a.name||a.form.getAttribute('name'),k=document.activeElement.name;if(!/^true$/i.test(c.$forms[b].isValidating)){var z=a.parentNode;a=!1;var y=z.getElementsByTagName('div');
|
|
288
289
|
/form\-item\-warning/.test(z.className)&&k!=e?z.className=z.className.replace(/form\-item\-warning/,'form-item-error'):/form\-item\-error/.test(z.className)&&k==e&&(z.className=z.className.replace(/form\-item\-error/,'form-item-warning'),a=!0);if(!/^true$/i.test(c.$forms[b].isValidating)||a)for(b=0,e=y.length;b<e;++b)if(/form\-item\-error\-message/.test(y[b].className)){y[b].className=a?y[b].className+' hidden':y[b].className.replace(/(\s+hidden|hidden)/,'');break}}},J={},L=function(a,b,e,k){if(A)var z=
|
|
289
290
|
null;var y=!1;if(typeof a.dataset.ginaFormIsResetting!='undefined'&&/^(true)$/i.test(a.dataset.ginaFormIsResetting))b={},J={},a.dataset.ginaFormIsResetting=!1;else if(/^(true)$/i.test(a.dataset.ginaFormLiveCheckEnabled)&&typeof k!='undefined'){var f=typeof a.id!='string'?a.getAttribute('id'):a.id;typeof J[f]=='undefined'&&(J[f]={});if(b&&b.count()>0){if(J[f][k]={},J[f][k]=D(b[k],J[f][k]),J[f][k].count()==0&&delete J[f][k],b=J[f],!c.$forms[f].sent||c.$forms[f].isValidating)if(y=!0,f=c.$forms[f].lastFocused,
|
|
Binary file
|
|
@@ -4954,7 +4954,21 @@ if ( /^local$/i.test(process.env.NODE_SCOPE) ) {
|
|
|
4954
4954
|
|
|
4955
4955
|
|
|
4956
4956
|
/**
|
|
4957
|
-
* Throw error
|
|
4957
|
+
* Throw error — terminates the request with an error response.
|
|
4958
|
+
*
|
|
4959
|
+
* Response shape depends on the request type:
|
|
4960
|
+
* - XHR / non-templated routes → JSON body `{ status, error, stack? }`
|
|
4961
|
+
* - Templated routes → HTML error page or rendered error template
|
|
4962
|
+
*
|
|
4963
|
+
* The `stack` field (JSON) and the `<pre class="stack">` block (fallback
|
|
4964
|
+
* HTML error page) are emitted only when the active scope is local
|
|
4965
|
+
* (`NODE_SCOPE_IS_LOCAL=true`) so the dev toolbar can render server-side
|
|
4966
|
+
* stack frames in its data-xhr panel and the fallback HTML page shows the
|
|
4967
|
+
* trace inline. Beta, testing, production, and unset scopes strip both to
|
|
4968
|
+
* prevent server-internals (file paths, library versions, internal frames)
|
|
4969
|
+
* from leaking to API clients or page viewers. Custom error templates
|
|
4970
|
+
* dispatched via `renderCustomError` are consumer-owned — what the
|
|
4971
|
+
* template renders from `req.params.errorObject` is the consumer's call.
|
|
4958
4972
|
*
|
|
4959
4973
|
* @param {object} [ res ]
|
|
4960
4974
|
* @param {number} code
|
|
@@ -5136,6 +5150,15 @@ if ( /^local$/i.test(process.env.NODE_SCOPE) ) {
|
|
|
5136
5150
|
}
|
|
5137
5151
|
}
|
|
5138
5152
|
|
|
5153
|
+
// Fail-closed: strip server-side stack from the JSON wire outside
|
|
5154
|
+
// local scope so file paths, library versions, and internal stack
|
|
5155
|
+
// frames don't leak to API clients. Local scope keeps it so the
|
|
5156
|
+
// dev toolbar's data-xhr panel can render it (events.js:394 →
|
|
5157
|
+
// ginaToolbar.update('data-xhr', XHRData)).
|
|
5158
|
+
if (!_isLocalScope && errorObject && errorObject.stack) {
|
|
5159
|
+
delete errorObject.stack;
|
|
5160
|
+
}
|
|
5161
|
+
|
|
5139
5162
|
var errOutput = null, output = errorObject.toString();
|
|
5140
5163
|
if ( output == '[object Object]' ) {
|
|
5141
5164
|
errOutput = JSON.stringify(errorObject);
|
|
@@ -5289,7 +5312,12 @@ if ( /^local$/i.test(process.env.NODE_SCOPE) ) {
|
|
|
5289
5312
|
msgString += '<pre class="'+ eCode +' message">'+ msg.message +'</pre>';
|
|
5290
5313
|
}
|
|
5291
5314
|
|
|
5292
|
-
|
|
5315
|
+
// Fail-closed: render the stack frame only in local scope
|
|
5316
|
+
// so file paths, library versions, and internal frames don't
|
|
5317
|
+
// leak via the fallback HTML error page. Symmetric to the
|
|
5318
|
+
// JSON wire gate (~L5147). Custom error templates dispatched
|
|
5319
|
+
// via renderCustomError at L5266-5281 are consumer-owned.
|
|
5320
|
+
if (msg.stack && _isLocalScope) {
|
|
5293
5321
|
|
|
5294
5322
|
if (msg.error) {
|
|
5295
5323
|
msg.stack = msg.stack.replace(msg.error, '')
|
|
@@ -5327,7 +5355,9 @@ if ( /^local$/i.test(process.env.NODE_SCOPE) ) {
|
|
|
5327
5355
|
if (message) {
|
|
5328
5356
|
msgString += '<pre class="'+ eCode +' message">'+ message +'</pre>';
|
|
5329
5357
|
}
|
|
5330
|
-
|
|
5358
|
+
// Fail-closed: local scope only — same gate shape as the
|
|
5359
|
+
// msg-shape site above.
|
|
5360
|
+
if (stack && _isLocalScope) {
|
|
5331
5361
|
msgString += '<pre class="'+ eCode +' stack">'+ stack +'</pre>';
|
|
5332
5362
|
}
|
|
5333
5363
|
}
|
|
@@ -1162,6 +1162,25 @@ isBundleMounted(projects, bundlesPath, getContext('bundle'), function onBundleMo
|
|
|
1162
1162
|
console.warn('[lib.metrics] init skipped: ' + (metricsErr.message || metricsErr));
|
|
1163
1163
|
}
|
|
1164
1164
|
|
|
1165
|
+
// #S7 — admin /_gina/* allowlist init. Reads `admin.allowFrom`
|
|
1166
|
+
// from app.json; defaults to loopback (127.0.0.1, ::1). Stored
|
|
1167
|
+
// on process.gina so server.isaac.js's /_gina/info and
|
|
1168
|
+
// /_gina/cache/stats handlers can gate on it. Same shape as the
|
|
1169
|
+
// metrics allowlist but a separate axis — admin endpoints expose
|
|
1170
|
+
// process state and warrant their own access control.
|
|
1171
|
+
try {
|
|
1172
|
+
var _adminAppConf = (typeof gna.getConfig === 'function') ? gna.getConfig('app') : null;
|
|
1173
|
+
var _adminAllow = (_adminAppConf && _adminAppConf.admin && Array.isArray(_adminAppConf.admin.allowFrom))
|
|
1174
|
+
? _adminAppConf.admin.allowFrom.slice()
|
|
1175
|
+
: ['127.0.0.1', '::1'];
|
|
1176
|
+
if (!process.gina) process.gina = {};
|
|
1177
|
+
process.gina._adminAllowList = _adminAllow;
|
|
1178
|
+
} catch (adminAclErr) {
|
|
1179
|
+
console.warn('[admin-acl] init skipped: ' + (adminAclErr.message || adminAclErr));
|
|
1180
|
+
if (!process.gina) process.gina = {};
|
|
1181
|
+
process.gina._adminAllowList = ['127.0.0.1', '::1'];
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1165
1184
|
// setting default global middlewares
|
|
1166
1185
|
if ( typeof(instance.use) == 'function' ) {
|
|
1167
1186
|
|
|
@@ -304,6 +304,18 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* getFormById
|
|
309
|
+
*
|
|
310
|
+
* @param {string} formId
|
|
311
|
+
*
|
|
312
|
+
* @returns {object} $form
|
|
313
|
+
*
|
|
314
|
+
* @throws {Error} When `formId` resolves via document.getElementById to a non-FORM
|
|
315
|
+
* element. Same shape and root cause as validateFormById's @throws —
|
|
316
|
+
* a sibling <p id="X"> / <div id="X"> shares the id with a later-
|
|
317
|
+
* loaded <form id="X"> (popin / AJAX fragment).
|
|
318
|
+
* */
|
|
307
319
|
var getFormById = function(formId) {
|
|
308
320
|
var $form = null, _id = formId;
|
|
309
321
|
|
|
@@ -317,8 +329,25 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
317
329
|
_id = _id.replace(/\#/, '');
|
|
318
330
|
|
|
319
331
|
// in case form is created on the fly and is not yet registered
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
var $candidate = document.getElementById(_id);
|
|
333
|
+
if ($candidate != null && typeof (instance['$forms'][_id]) == 'undefined') {
|
|
334
|
+
|
|
335
|
+
// Same fail-loud guard as validateFormById's else branch —
|
|
336
|
+
// document.getElementById returns the first matching element regardless
|
|
337
|
+
// of tag, so a sibling <p id="X"> / <div id="X"> / etc. wins over a
|
|
338
|
+
// later-loaded <form id="X">. Surface the collision instead of letting
|
|
339
|
+
// initForm register the non-FORM element in instance.$forms (polluting
|
|
340
|
+
// subsequent lookups) and crash later inside bindForm with
|
|
341
|
+
// `Cannot read properties of undefined (reading 'length')`.
|
|
342
|
+
if ( !($candidate instanceof HTMLFormElement) ) {
|
|
343
|
+
throw new Error(
|
|
344
|
+
'[ FormValidator::getFormById(formId) ] `' + _id + '` resolves to <'
|
|
345
|
+
+ $candidate.tagName + '>, not a FORM. A non-FORM element shares the same id as '
|
|
346
|
+
+ 'the target form — rename one of them so the id is unique.'
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
initForm( $candidate );
|
|
322
351
|
}
|
|
323
352
|
|
|
324
353
|
if ( typeof(instance.$forms[_id]) != 'undefined' ) {
|
|
@@ -390,6 +419,12 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
390
419
|
* @param {object} [customRule]
|
|
391
420
|
*
|
|
392
421
|
* @returns {object} $form
|
|
422
|
+
*
|
|
423
|
+
* @throws {Error} When `formId` resolves via document.getElementById to a non-FORM
|
|
424
|
+
* element (e.g. a sibling <p id="X"> / <div id="X"> sharing the id
|
|
425
|
+
* with a later-loaded <form id="X">). Surfaces the collision instead
|
|
426
|
+
* of crashing later inside bindForm with a cryptic
|
|
427
|
+
* `Cannot read properties of undefined (reading 'length')`.
|
|
393
428
|
* */
|
|
394
429
|
var validateFormById = function(formId, customRule) {
|
|
395
430
|
var $form = null
|
|
@@ -433,6 +468,21 @@ function ValidatorPlugin(rules, data, formId) {
|
|
|
433
468
|
$form = this.$forms[_id] = instance['$forms'][_id];
|
|
434
469
|
} else { // binding a form out of context (outside of the main instance)
|
|
435
470
|
$target = document.getElementById(_id);
|
|
471
|
+
|
|
472
|
+
// validateFormById was designed for FORM elements. getElementById
|
|
473
|
+
// returns the first matching element regardless of tag, so a sibling
|
|
474
|
+
// <p id="X"> / <div id="X"> / etc. wins over a later-loaded
|
|
475
|
+
// <form id="X">. Surface the collision instead of crashing later
|
|
476
|
+
// inside bindForm with a cryptic
|
|
477
|
+
// `Cannot read properties of undefined (reading 'length')`.
|
|
478
|
+
if ( $target && !($target instanceof HTMLFormElement) ) {
|
|
479
|
+
throw new Error(
|
|
480
|
+
'[ FormValidator::validateFormById(formId) ] `' + _id + '` resolves to <'
|
|
481
|
+
+ $target.tagName + '>, not a FORM. A non-FORM element shares the same id as '
|
|
482
|
+
+ 'the target form — rename one of them so the id is unique.'
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
436
486
|
$validator.id = _id;
|
|
437
487
|
$validator.target = $target;
|
|
438
488
|
|
|
@@ -118,9 +118,15 @@ function Router(env, scope) {
|
|
|
118
118
|
// removed: direct pre-load of controller.js — index.js (in dev/cacheless mode) deletes and
|
|
119
119
|
// re-requires controller.js internally, so pre-loading it here caused a double instantiation
|
|
120
120
|
// (Domain constructor, swig init, etc.) on every request. Let index.js own the re-require.
|
|
121
|
-
require.cache[_(corePath +'/controller/index.js', true)] = require( _(corePath +'/controller/index.js', true) );
|
|
122
121
|
|
|
123
|
-
|
|
122
|
+
// #B18 — Use the return value of require() directly instead of
|
|
123
|
+
// re-poisoning the cache slot with `require.cache[path] = require(path)`.
|
|
124
|
+
// The latter assigns the exports OBJECT (no `.exports` key) into the slot
|
|
125
|
+
// where Node expects a Module instance; downstream plain `require()`
|
|
126
|
+
// calls then read `.exports` off a bare exports object and get
|
|
127
|
+
// `undefined`. router.js was the last latent occurrence of this
|
|
128
|
+
// antipattern after the `refreshCore()` fix (`add6655e`).
|
|
129
|
+
SuperController = require(_(corePath +'/controller/index.js', true));
|
|
124
130
|
|
|
125
131
|
if (_hotReload) _hotReload.core = false; // #M6 — clear flag after eviction
|
|
126
132
|
corePath = null;
|
|
@@ -615,12 +621,13 @@ function Router(env, scope) {
|
|
|
615
621
|
//if (isCacheless) {
|
|
616
622
|
// Super controller
|
|
617
623
|
delete require.cache[require.resolve(_(corePath +'/controller/index.js', true))];
|
|
618
|
-
require.cache[_(corePath +'/controller/index.js', true)] = require( _(corePath +'/controller/index.js', true) );
|
|
619
|
-
|
|
620
624
|
delete require.cache[require.resolve(filename)];
|
|
621
625
|
//}
|
|
622
626
|
|
|
623
|
-
|
|
627
|
+
// #B18 — Same fix as the sibling at L116-127. require() returns
|
|
628
|
+
// a freshly built Module instance; assigning the exports object
|
|
629
|
+
// back into require.cache[path] poisons the slot.
|
|
630
|
+
var SuperController = require(_(corePath +'/controller/index.js', true));
|
|
624
631
|
|
|
625
632
|
|
|
626
633
|
var RequiredController = require(filename);
|
|
@@ -41,6 +41,42 @@ const env = process.env.NODE_ENV
|
|
|
41
41
|
, isProductionScope = process.env.NODE_SCOPE_IS_PRODUCTION && process.env.NODE_SCOPE_IS_PRODUCTION.toLowerCase() === 'true'
|
|
42
42
|
;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* IP-allowlist check for admin-grade /_gina/* endpoints (/_gina/info,
|
|
46
|
+
* /_gina/cache/stats). #S7.
|
|
47
|
+
*
|
|
48
|
+
* Reads the allowlist from `process.gina._adminAllowList`, which `gna.js`
|
|
49
|
+
* populates at bundle init from `app.json` `admin.allowFrom` (defaults to
|
|
50
|
+
* loopback `['127.0.0.1', '::1']`). Same shape as `lib.metrics.isClientAllowed`
|
|
51
|
+
* but on a separate axis — admin endpoints expose process state (memory,
|
|
52
|
+
* uptime, HTTP/2 session counters, cache contents) and are gated separately
|
|
53
|
+
* from Prometheus scrapes.
|
|
54
|
+
*
|
|
55
|
+
* Reads the client IP from `req.socket.remoteAddress` only — never trusts
|
|
56
|
+
* `X-Forwarded-For` because reverse proxies could spoof it. Normalises
|
|
57
|
+
* `::ffff:IPv4` (IPv6-mapped IPv4) → `IPv4` so listing `127.0.0.1` matches
|
|
58
|
+
* both forms.
|
|
59
|
+
*
|
|
60
|
+
* Empty allowlist (`[]`) denies everyone — explicit lockdown choice.
|
|
61
|
+
* Missing `process.gina._adminAllowList` (init not yet fired) falls back
|
|
62
|
+
* to loopback-only, the safest default.
|
|
63
|
+
*
|
|
64
|
+
* @inner
|
|
65
|
+
* @param {http.IncomingMessage|http2.Http2ServerRequest} req
|
|
66
|
+
* @returns {boolean} true if client IP is allowed, false otherwise
|
|
67
|
+
*/
|
|
68
|
+
function isAdminClientAllowed(req) {
|
|
69
|
+
var list = (typeof process.gina === 'object' && process.gina && Array.isArray(process.gina._adminAllowList))
|
|
70
|
+
? process.gina._adminAllowList
|
|
71
|
+
: ['127.0.0.1', '::1'];
|
|
72
|
+
if (list.length === 0) return false;
|
|
73
|
+
var ip = (req.socket && req.socket.remoteAddress)
|
|
74
|
+
|| (req.connection && req.connection.remoteAddress)
|
|
75
|
+
|| '';
|
|
76
|
+
if (ip.indexOf('::ffff:') === 0) ip = ip.slice(7);
|
|
77
|
+
return list.indexOf(ip) >= 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
44
80
|
/**
|
|
45
81
|
* Reloads all core and lib modules from disk by replacing their require.cache
|
|
46
82
|
* entries with fresh exports. Excludes gna.js itself. Also refreshes the
|
|
@@ -663,6 +699,25 @@ function ServerEngineClass(options) {
|
|
|
663
699
|
|
|
664
700
|
if ( request.method.toUpperCase() === 'GET' && /\_gina\/info$/i.test(request.url) ) {
|
|
665
701
|
|
|
702
|
+
// #S7 — IP allowlist gate. Mirrors the metrics endpoint
|
|
703
|
+
// gate at L605-621. 403 on deny.
|
|
704
|
+
if ( !isAdminClientAllowed(request) ) {
|
|
705
|
+
var infoForbiddenBody = JSON.stringify({ error: 'forbidden', message: '/_gina/info: client IP not in app.json admin.allowFrom' });
|
|
706
|
+
var infoForbiddenHeaders = {
|
|
707
|
+
'cache-control': 'no-cache, no-store, must-revalidate',
|
|
708
|
+
'pragma': 'no-cache',
|
|
709
|
+
'expires': '0',
|
|
710
|
+
'content-type': 'application/json; charset=utf8',
|
|
711
|
+
'X-Powered-By': 'Gina/' + GINA_VERSION
|
|
712
|
+
};
|
|
713
|
+
if (response.stream) {
|
|
714
|
+
response.stream.respond({ ':status': 403, ...infoForbiddenHeaders });
|
|
715
|
+
return response.stream.end(infoForbiddenBody);
|
|
716
|
+
}
|
|
717
|
+
response.writeHead(403, infoForbiddenHeaders);
|
|
718
|
+
return response.end(infoForbiddenBody);
|
|
719
|
+
}
|
|
720
|
+
|
|
666
721
|
var infoPayload = {
|
|
667
722
|
"cache-is-enabled": server._cacheIsEnabled,
|
|
668
723
|
"memory" : process.memoryUsage(),
|
|
@@ -705,6 +760,25 @@ function ServerEngineClass(options) {
|
|
|
705
760
|
}
|
|
706
761
|
|
|
707
762
|
if ( request.method.toUpperCase() === 'GET' && /\/_gina\/cache\/stats$/i.test(request.url) ) {
|
|
763
|
+
|
|
764
|
+
// #S7 — IP allowlist gate. Same shape as the /_gina/info gate above.
|
|
765
|
+
if ( !isAdminClientAllowed(request) ) {
|
|
766
|
+
var cacheStatsForbiddenBody = JSON.stringify({ error: 'forbidden', message: '/_gina/cache/stats: client IP not in app.json admin.allowFrom' });
|
|
767
|
+
var cacheStatsForbiddenHeaders = {
|
|
768
|
+
'cache-control': 'no-cache, no-store, must-revalidate',
|
|
769
|
+
'pragma': 'no-cache',
|
|
770
|
+
'expires': '0',
|
|
771
|
+
'content-type': 'application/json; charset=utf8',
|
|
772
|
+
'X-Powered-By': 'Gina/' + GINA_VERSION
|
|
773
|
+
};
|
|
774
|
+
if (response.stream) {
|
|
775
|
+
response.stream.respond({ ':status': 403, ...cacheStatsForbiddenHeaders });
|
|
776
|
+
return response.stream.end(cacheStatsForbiddenBody);
|
|
777
|
+
}
|
|
778
|
+
response.writeHead(403, cacheStatsForbiddenHeaders);
|
|
779
|
+
return response.end(cacheStatsForbiddenBody);
|
|
780
|
+
}
|
|
781
|
+
|
|
708
782
|
cache.from(server._cached);
|
|
709
783
|
const cacheStatsData = JSON.stringify(cache.stats());
|
|
710
784
|
const cacheStatsHeaders = {
|
|
@@ -48,12 +48,23 @@ function List(opt, cmd) {
|
|
|
48
48
|
// Tolerant — fall through with empty ports table.
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
// Full pre-scan of argv. No dispatch inside the loop — separating
|
|
52
|
+
// parsing from dispatch means flag ordering can't change behaviour
|
|
53
|
+
// (`--all --format=json` now produces the same JSON output as
|
|
54
|
+
// `--format=json --all`). The previous in-loop short-circuit on
|
|
55
|
+
// `!self.projectName` was also the root of the spurious
|
|
56
|
+
// `[ null ] is not a valid project name` error on the bare
|
|
57
|
+
// `gina bundle:list` no-arg call: CmdHelper initialises
|
|
58
|
+
// `cmd.projectName` to `null` (filterArgs only assigns from a
|
|
59
|
+
// `@<project>` argv token, per `lib/cmd/helper.js:77`), and the
|
|
60
|
+
// original post-loop dispatch only handled `undefined`.
|
|
61
|
+
var allFlag = false;
|
|
51
62
|
for (let i=3, len=process.argv.length; i<len; i++) {
|
|
52
63
|
if ( /^\-\-format\=/.test(process.argv[i]) ) {
|
|
53
|
-
self.format = process.argv[i].split(/\=/)[1]
|
|
64
|
+
self.format = process.argv[i].split(/\=/)[1];
|
|
54
65
|
}
|
|
55
|
-
if ( /^\-\-all
|
|
56
|
-
|
|
66
|
+
if ( /^\-\-all(\=|$)/.test(process.argv[i]) ) {
|
|
67
|
+
allFlag = true;
|
|
57
68
|
}
|
|
58
69
|
}
|
|
59
70
|
|
|
@@ -73,16 +84,19 @@ function List(opt, cmd) {
|
|
|
73
84
|
// }
|
|
74
85
|
// }
|
|
75
86
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
// Dispatch after the full scan. `self.projectName == null` matches
|
|
88
|
+
// both null (CmdHelper default) and undefined — either signals "no
|
|
89
|
+
// project specified", route to listAll().
|
|
90
|
+
if ( allFlag || self.projectName == null ) {
|
|
91
|
+
listAll();
|
|
92
|
+
} else if ( isDefined(self.projectName) ) {
|
|
93
|
+
listProjectOnly();
|
|
80
94
|
} else {
|
|
81
95
|
console.error('[ '+self.projectName+' ] is not a valid project name.');
|
|
82
|
-
process.exit(1)
|
|
96
|
+
process.exit(1);
|
|
83
97
|
}
|
|
84
98
|
|
|
85
|
-
process.exit(0)
|
|
99
|
+
process.exit(0);
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
/**
|