@portel/photon-core 2.1.2 → 2.2.0

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 (61) hide show
  1. package/README.md +61 -0
  2. package/dist/base.d.ts +41 -1
  3. package/dist/base.d.ts.map +1 -1
  4. package/dist/base.js +63 -1
  5. package/dist/base.js.map +1 -1
  6. package/dist/channels/daemon-broker.d.ts +35 -0
  7. package/dist/channels/daemon-broker.d.ts.map +1 -0
  8. package/dist/channels/daemon-broker.js +229 -0
  9. package/dist/channels/daemon-broker.js.map +1 -0
  10. package/dist/channels/http-broker.d.ts +45 -0
  11. package/dist/channels/http-broker.d.ts.map +1 -0
  12. package/dist/channels/http-broker.js +182 -0
  13. package/dist/channels/http-broker.js.map +1 -0
  14. package/dist/channels/index.d.ts +53 -0
  15. package/dist/channels/index.d.ts.map +1 -0
  16. package/dist/channels/index.js +67 -0
  17. package/dist/channels/index.js.map +1 -0
  18. package/dist/channels/noop-broker.d.ts +21 -0
  19. package/dist/channels/noop-broker.d.ts.map +1 -0
  20. package/dist/channels/noop-broker.js +38 -0
  21. package/dist/channels/noop-broker.js.map +1 -0
  22. package/dist/channels/redis-broker.d.ts +45 -0
  23. package/dist/channels/redis-broker.d.ts.map +1 -0
  24. package/dist/channels/redis-broker.js +214 -0
  25. package/dist/channels/redis-broker.js.map +1 -0
  26. package/dist/channels/registry.d.ts +49 -0
  27. package/dist/channels/registry.d.ts.map +1 -0
  28. package/dist/channels/registry.js +150 -0
  29. package/dist/channels/registry.js.map +1 -0
  30. package/dist/channels/types.d.ts +85 -0
  31. package/dist/channels/types.d.ts.map +1 -0
  32. package/dist/channels/types.js +8 -0
  33. package/dist/channels/types.js.map +1 -0
  34. package/dist/decorators.d.ts +48 -0
  35. package/dist/decorators.d.ts.map +1 -0
  36. package/dist/decorators.js +64 -0
  37. package/dist/decorators.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +6 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/schema-extractor.d.ts +23 -2
  43. package/dist/schema-extractor.d.ts.map +1 -1
  44. package/dist/schema-extractor.js +88 -3
  45. package/dist/schema-extractor.js.map +1 -1
  46. package/dist/types.d.ts +18 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js.map +1 -1
  49. package/package.json +5 -3
  50. package/src/base.ts +70 -1
  51. package/src/channels/daemon-broker.ts +271 -0
  52. package/src/channels/http-broker.ts +221 -0
  53. package/src/channels/index.ts +96 -0
  54. package/src/channels/noop-broker.ts +47 -0
  55. package/src/channels/redis-broker.ts +252 -0
  56. package/src/channels/registry.ts +170 -0
  57. package/src/channels/types.ts +95 -0
  58. package/src/decorators.ts +87 -0
  59. package/src/index.ts +13 -0
  60. package/src/schema-extractor.ts +100 -3
  61. package/src/types.ts +23 -0
@@ -142,6 +142,11 @@ export class SchemaExtractor {
142
142
  const isStateful = this.hasStatefulTag(jsdoc);
143
143
  const autorun = this.hasAutorunTag(jsdoc);
144
144
 
145
+ // Daemon features
146
+ const webhook = this.extractWebhook(jsdoc, methodName);
147
+ const scheduled = this.extractScheduled(jsdoc, methodName);
148
+ const locked = this.extractLocked(jsdoc, methodName);
149
+
145
150
  tools.push({
146
151
  name: methodName,
147
152
  description,
@@ -154,6 +159,10 @@ export class SchemaExtractor {
154
159
  ...(yields && yields.length > 0 ? { yields } : {}),
155
160
  ...(isStateful ? { isStateful: true } : {}),
156
161
  ...(autorun ? { autorun: true } : {}),
162
+ // Daemon features
163
+ ...(webhook !== undefined ? { webhook } : {}),
164
+ ...(scheduled ? { scheduled } : {}),
165
+ ...(locked !== undefined ? { locked } : {}),
157
166
  });
158
167
  }
159
168
  };
@@ -699,6 +708,8 @@ export class SchemaExtractor {
699
708
  .replace(/\{@label\s+[^}]+\}/g, '')
700
709
  .replace(/\{@placeholder\s+[^}]+\}/g, '')
701
710
  .replace(/\{@hint\s+[^}]+\}/g, '')
711
+ .replace(/\{@hidden\s*\}/g, '')
712
+ .replace(/\{@accept\s+[^}]+\}/g, '')
702
713
  .replace(/\s+/g, ' ') // Collapse multiple spaces
703
714
  .trim();
704
715
  paramDocs.set(paramName, cleanDesc);
@@ -710,7 +721,7 @@ export class SchemaExtractor {
710
721
  /**
711
722
  * Extract parameter constraints from JSDoc @param tags
712
723
  * Supports inline tags: {@min}, {@max}, {@pattern}, {@format}, {@default}, {@unique},
713
- * {@example}, {@multipleOf}, {@deprecated}, {@readOnly}, {@writeOnly}
724
+ * {@example}, {@multipleOf}, {@deprecated}, {@readOnly}, {@writeOnly}, {@accept}
714
725
  */
715
726
  private extractParamConstraints(jsdocContent: string): Map<string, any> {
716
727
  const constraints = new Map<string, any>();
@@ -847,6 +858,12 @@ export class SchemaExtractor {
847
858
  paramConstraints.hidden = true;
848
859
  }
849
860
 
861
+ // Extract {@accept pattern} - file type filter for file picker (e.g., "*.ts,*.js" or ".ts,.js")
862
+ const acceptMatch = description.match(/\{@accept\s+([^}]+)\}/);
863
+ if (acceptMatch) {
864
+ paramConstraints.accept = acceptMatch[1].trim();
865
+ }
866
+
850
867
  if (Object.keys(paramConstraints).length > 0) {
851
868
  constraints.set(paramName, paramConstraints);
852
869
  }
@@ -957,6 +974,10 @@ export class SchemaExtractor {
957
974
  if (constraints.hidden === true) {
958
975
  s.hidden = true;
959
976
  }
977
+ // Apply accept pattern for file picker filtering
978
+ if (constraints.accept !== undefined) {
979
+ s.accept = constraints.accept;
980
+ }
960
981
  };
961
982
 
962
983
  // Apply to anyOf schemas or direct schema
@@ -1006,6 +1027,79 @@ export class SchemaExtractor {
1006
1027
  return /@autorun/i.test(jsdocContent);
1007
1028
  }
1008
1029
 
1030
+ // ═══════════════════════════════════════════════════════════════════════════════
1031
+ // DAEMON FEATURE EXTRACTION
1032
+ // ═══════════════════════════════════════════════════════════════════════════════
1033
+
1034
+ /**
1035
+ * Extract webhook configuration from @webhook tag or handle* prefix
1036
+ * - @webhook → use method name as path
1037
+ * - @webhook stripe → custom path "stripe"
1038
+ * - handle* prefix → auto-detected as webhook
1039
+ */
1040
+ private extractWebhook(jsdocContent: string, methodName: string): boolean | string | undefined {
1041
+ // Check for @webhook tag with optional path
1042
+ // Path must start with a word character (to exclude JSDoc asterisks and closing)
1043
+ const webhookMatch = jsdocContent.match(/@webhook(?:\s+(\w[\w\-\/]*))?/i);
1044
+ if (webhookMatch) {
1045
+ const path = webhookMatch[1]?.trim();
1046
+ // Return custom path if specified, otherwise true for bare @webhook
1047
+ return path || true;
1048
+ }
1049
+
1050
+ // Check for handle* prefix (convention)
1051
+ if (methodName.startsWith('handle')) {
1052
+ return true;
1053
+ }
1054
+
1055
+ return undefined;
1056
+ }
1057
+
1058
+ /**
1059
+ * Extract scheduled cron expression from @scheduled, @cron tag, or scheduled* prefix
1060
+ * - @scheduled 0 0 * * * → cron expression
1061
+ * - @cron 0 0 * * * → cron expression
1062
+ * - scheduled* prefix requires @cron tag for the expression
1063
+ */
1064
+ private extractScheduled(jsdocContent: string, methodName: string): string | undefined {
1065
+ // Check for @scheduled with cron expression
1066
+ const scheduledMatch = jsdocContent.match(/@scheduled\s+([*\d,\-\/]+(?:\s+[*\d,\-\/]+){4})/i);
1067
+ if (scheduledMatch) {
1068
+ return scheduledMatch[1].trim();
1069
+ }
1070
+
1071
+ // Check for @cron tag
1072
+ const cronMatch = jsdocContent.match(/@cron\s+([*\d,\-\/]+(?:\s+[*\d,\-\/]+){4})/i);
1073
+ if (cronMatch) {
1074
+ return cronMatch[1].trim();
1075
+ }
1076
+
1077
+ // scheduled* prefix without explicit cron - method exists but needs @cron
1078
+ if (methodName.startsWith('scheduled') && !cronMatch && !scheduledMatch) {
1079
+ // Could log warning: scheduled* method missing @cron tag
1080
+ return undefined;
1081
+ }
1082
+
1083
+ return undefined;
1084
+ }
1085
+
1086
+ /**
1087
+ * Extract lock configuration from @locked tag
1088
+ * - @locked → use method name as lock
1089
+ * - @locked board:write → custom lock name
1090
+ */
1091
+ private extractLocked(jsdocContent: string, methodName: string): boolean | string | undefined {
1092
+ // Lock name must start with a word character (to exclude JSDoc asterisks and closing)
1093
+ const lockedMatch = jsdocContent.match(/@locked(?:\s+(\w[\w\-:]*))?/i);
1094
+ if (lockedMatch) {
1095
+ const lockName = lockedMatch[1]?.trim();
1096
+ // Return custom lock name if specified, otherwise true for bare @locked
1097
+ return lockName || true;
1098
+ }
1099
+
1100
+ return undefined;
1101
+ }
1102
+
1009
1103
  /**
1010
1104
  * Extract URI pattern from @Static tag
1011
1105
  * Example: @Static github://repos/{owner}/{repo}/readme
@@ -1093,13 +1187,16 @@ export class SchemaExtractor {
1093
1187
  }
1094
1188
 
1095
1189
  /**
1096
- * Extract icon from @icon tag
1190
+ * Extract icon from @icon tag (standalone, not inside layout hints)
1097
1191
  * Example: @icon calculator
1098
1192
  * Example: @icon 🧮
1099
1193
  * Example: @icon mdi:calculator
1194
+ * Note: Does NOT match @icon inside layout hints like {@icon fieldname}
1100
1195
  */
1101
1196
  private extractIcon(jsdocContent: string): string | undefined {
1102
- const iconMatch = jsdocContent.match(/@icon\s+([^\s@*]+)/i);
1197
+ // First, remove layout hints blocks to avoid matching @icon inside them
1198
+ const withoutLayoutHints = jsdocContent.replace(/\{[^}]+\}/g, '');
1199
+ const iconMatch = withoutLayoutHints.match(/@icon\s+([^\s@*,]+)/i);
1103
1200
  if (iconMatch) {
1104
1201
  return iconMatch[1].trim();
1105
1202
  }
package/src/types.ts CHANGED
@@ -63,6 +63,29 @@ export interface ExtractedSchema {
63
63
  isStateful?: boolean;
64
64
  /** True if this method should auto-execute when selected (idempotent, no required params) */
65
65
  autorun?: boolean;
66
+
67
+ // ═══ DAEMON FEATURES ═══
68
+
69
+ /**
70
+ * Webhook endpoint path (from @webhook tag or handle* prefix)
71
+ * - true: use method name as path (e.g., handleGithubPush → /webhook/handleGithubPush)
72
+ * - string: custom path (e.g., @webhook stripe → /webhook/stripe)
73
+ */
74
+ webhook?: boolean | string;
75
+
76
+ /**
77
+ * Cron schedule expression (from @scheduled or @cron tag, or scheduled* prefix)
78
+ * Standard 5-field format: minute hour day-of-month month day-of-week
79
+ * Example: "0 0 * * *" (daily at midnight)
80
+ */
81
+ scheduled?: string;
82
+
83
+ /**
84
+ * Distributed lock name (from @locked tag)
85
+ * - true: use method name as lock (e.g., batchUpdate → lock "batchUpdate")
86
+ * - string: custom lock name (e.g., @locked board:write)
87
+ */
88
+ locked?: boolean | string;
66
89
  }
67
90
 
68
91
  export interface PhotonMCPClass {