webspresso 0.0.93 → 0.0.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/bin/commands/polar-migrate.js +50 -0
  2. package/bin/webspresso.js +2 -0
  3. package/core/auth/tokens.js +16 -0
  4. package/core/orm/cache/layer.js +1 -1
  5. package/core/orm/index.js +4 -1
  6. package/core/orm/transaction.js +3 -1
  7. package/core/ssr/stream.js +58 -14
  8. package/package.json +15 -4
  9. package/plugins/admin-panel/auth.js +10 -0
  10. package/plugins/content/index.js +5 -3
  11. package/plugins/data-exchange/import.js +76 -39
  12. package/plugins/index.js +2 -0
  13. package/plugins/mcp/transports/sse.js +23 -6
  14. package/plugins/polar/.env.example +12 -0
  15. package/plugins/polar/CHANGELOG.md +56 -0
  16. package/plugins/polar/README.md +181 -0
  17. package/plugins/polar/index.js +149 -0
  18. package/plugins/polar/src/api.js +54 -0
  19. package/plugins/polar/src/billing-hooks.js +51 -0
  20. package/plugins/polar/src/checkout.js +111 -0
  21. package/plugins/polar/src/config.js +90 -0
  22. package/plugins/polar/src/env.schema.js +36 -0
  23. package/plugins/polar/src/middleware.js +37 -0
  24. package/plugins/polar/src/migration.js +58 -0
  25. package/plugins/polar/src/nunjucks-filters.js +56 -0
  26. package/plugins/polar/src/portal.js +51 -0
  27. package/plugins/polar/src/rate-limit.js +138 -0
  28. package/plugins/polar/src/routes.js +213 -0
  29. package/plugins/polar/src/sync.js +138 -0
  30. package/plugins/polar/src/urls.js +141 -0
  31. package/plugins/polar/src/user-resolver.js +199 -0
  32. package/plugins/polar/src/webhooks.js +248 -0
  33. package/plugins/rate-limit/index.js +5 -1
  34. package/src/file-router.js +19 -2
  35. package/src/helpers.js +60 -13
  36. package/templates/skills/webspresso-usage/REFERENCE-framework.md +1 -0
package/src/helpers.js CHANGED
@@ -616,31 +616,54 @@ function createHelpers(ctx) {
616
616
  // ============================================
617
617
 
618
618
  /**
619
- * Get injected head content
619
+ * Get CSP nonce if available from request context
620
+ * @returns {string} Nonce string or empty string
621
+ */
622
+ nonce() {
623
+ return (
624
+ req?.res?.locals?.cspNonce ||
625
+ req?.res?.locals?.nonce ||
626
+ res?.locals?.cspNonce ||
627
+ res?.locals?.nonce ||
628
+ req?.cspNonce ||
629
+ req?.nonce ||
630
+ ''
631
+ );
632
+ },
633
+
634
+ /**
635
+ * Get injected head content (CSS, scripts)
636
+ * @param {Object|string} [options] - Optional { nonce: string } or nonce string
620
637
  * @returns {string} HTML content for head section
621
638
  */
622
- injectHead() {
639
+ injectHead(options) {
640
+ const explicitNonce = typeof options === 'string' ? options : options?.nonce;
641
+ const nonce = explicitNonce || this.nonce();
623
642
  const injector = getScriptInjector();
624
643
  let content = '';
625
644
 
626
645
  // Add styles
627
- const styles = injector.getStylesContent();
646
+ const styles = injector.getStylesContent(nonce ? { nonce } : options);
628
647
  if (styles) {
629
- content += `<style id="webspresso-injected-styles">\n${styles}\n</style>\n`;
648
+ const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
649
+ content += `<style id="webspresso-injected-styles"${nonceAttr}>\n${styles}\n</style>\n`;
630
650
  }
631
651
 
632
652
  // Add head scripts
633
- content += injector.getHeadContent();
653
+ content += injector.getHeadContent(nonce ? { nonce } : options);
634
654
 
635
655
  return content;
636
656
  },
637
657
 
638
658
  /**
639
659
  * Get injected body content (for end of body)
660
+ * @param {Object|string} [options] - Optional { nonce: string } or nonce string
640
661
  * @returns {string} HTML content for body end
641
662
  */
642
- injectBody() {
643
- return getScriptInjector().getBodyContent();
663
+ injectBody(options) {
664
+ const explicitNonce = typeof options === 'string' ? options : options?.nonce;
665
+ const nonce = explicitNonce || this.nonce();
666
+ return getScriptInjector().getBodyContent(nonce ? { nonce } : options);
644
667
  },
645
668
 
646
669
  /**
@@ -726,25 +749,49 @@ class ScriptInjector {
726
749
 
727
750
  /**
728
751
  * Get head content sorted by priority
752
+ * @param {Object|string} [options] - Optional { nonce: string } or nonce string
729
753
  */
730
- getHeadContent() {
754
+ getHeadContent(options = {}) {
755
+ const nonce = typeof options === 'string' ? options : (options?.nonce || '');
731
756
  const sorted = [...this.headScripts].sort((a, b) => b.priority - a.priority);
732
- return sorted.map(s => s.content).join('\n');
757
+ return sorted.map(s => {
758
+ let content = typeof s.content === 'function' ? s.content(options) : s.content;
759
+ if (nonce && typeof content === 'string') {
760
+ content = content.replace(/<script(?![^>]*\bnonce=)/gi, `<script nonce="${nonce}"`);
761
+ }
762
+ return content;
763
+ }).join('\n');
733
764
  }
734
765
 
735
766
  /**
736
767
  * Get body content sorted by priority
768
+ * @param {Object|string} [options] - Optional { nonce: string } or nonce string
737
769
  */
738
- getBodyContent() {
770
+ getBodyContent(options = {}) {
771
+ const nonce = typeof options === 'string' ? options : (options?.nonce || '');
739
772
  const sorted = [...this.bodyScripts].sort((a, b) => b.priority - a.priority);
740
- return sorted.map(s => s.content).join('\n');
773
+ return sorted.map(s => {
774
+ let content = typeof s.content === 'function' ? s.content(options) : s.content;
775
+ if (nonce && typeof content === 'string') {
776
+ content = content.replace(/<script(?![^>]*\bnonce=)/gi, `<script nonce="${nonce}"`);
777
+ }
778
+ return content;
779
+ }).join('\n');
741
780
  }
742
781
 
743
782
  /**
744
783
  * Get styles content
784
+ * @param {Object|string} [options] - Optional { nonce: string } or nonce string
745
785
  */
746
- getStylesContent() {
747
- return this.styles.map(s => s.content).join('\n');
786
+ getStylesContent(options = {}) {
787
+ const nonce = typeof options === 'string' ? options : (options?.nonce || '');
788
+ return this.styles.map(s => {
789
+ let content = typeof s.content === 'function' ? s.content(options) : s.content;
790
+ if (nonce && typeof content === 'string') {
791
+ content = content.replace(/<style(?![^>]*\bnonce=)/gi, `<style nonce="${nonce}"`);
792
+ }
793
+ return content;
794
+ }).join('\n');
748
795
  }
749
796
 
750
797
  /**
@@ -213,6 +213,7 @@ Pass **`db`** into **`createApp({ db })`** so **`ctx.db`** works in pages and pl
213
213
  | `auditLogPlugin` | Admin mutation audit trail |
214
214
  | `recaptchaPlugin` | v2/v3 + middleware |
215
215
  | `emailPlugin` | MJML + Nodemailer sending with optional admin UI and auth bridge |
216
+ | `polarPlugin` | Polar.sh billing: checkout, portal, webhooks, tier sync (`webspresso polar:migrate`) |
216
217
  | `seoCheckerPlugin` | Dev SEO panel |
217
218
  | `restResourcePlugin` | Opt-in REST CRUD from models; `?include=` uses ORM eager load (single-level relations only) |
218
219
  | `ormCacheAdminPlugin` | Admin page for ORM cache metrics / purge / invalidate (`db.cache` required) |