@rebasepro/common 0.26.0 → 0.27.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.
@@ -8,10 +8,11 @@ export declare function registerConditionOperations(): void;
8
8
  * Evaluate a condition against the given context.
9
9
  *
10
10
  * A condition may be stated as a literal instead of a rule — `hidden: true`
11
- * rather than `hidden: { "==": [1, 1] }` — and a literal is already its own
12
- * answer, so it is returned rather than handed to the evaluator.
11
+ * rather than `hidden: { "==": [1, 1] }`, a bound as `min: 0` — and a literal
12
+ * is already its own answer, so it is returned rather than handed to the
13
+ * evaluator.
13
14
  */
14
- export declare function evaluateCondition(rule: ConditionRule, context: ConditionContext): unknown;
15
+ export declare function evaluateCondition(rule: ConditionRule | number, context: ConditionContext): unknown;
15
16
  /**
16
17
  * Build a ConditionContext from the current property resolution context.
17
18
  */
@@ -41,11 +41,12 @@ function registerConditionOperations() {
41
41
  * Evaluate a condition against the given context.
42
42
  *
43
43
  * A condition may be stated as a literal instead of a rule — `hidden: true`
44
- * rather than `hidden: { "==": [1, 1] }` — and a literal is already its own
45
- * answer, so it is returned rather than handed to the evaluator.
44
+ * rather than `hidden: { "==": [1, 1] }`, a bound as `min: 0` — and a literal
45
+ * is already its own answer, so it is returned rather than handed to the
46
+ * evaluator.
46
47
  */
47
48
  function evaluateCondition(rule, context) {
48
- if (typeof rule === "boolean") return rule;
49
+ if (typeof rule === "boolean" || typeof rule === "number") return rule;
49
50
  registerConditionOperations();
50
51
  return jsonLogic.apply(rule, context);
51
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conditions.es.js","names":[],"sources":["../../src/util/conditions.ts"],"sourcesContent":["import jsonLogic from \"json-logic-js\";\nimport {\n ArrayProperty,\n AuthState,\n ConditionContext,\n ConditionRule,\n EnumValueConfig,\n NumberProperty,\n PropertyConditions,\n Property,\n ReferenceProperty,\n StringProperty\n} from \"@rebasepro/types\";\n\n/**\n * Access a nested property from an object via dot notation.\n */\nfunction getIn(obj: Record<string, unknown> | unknown, path: string): unknown {\n if (!obj || !path) return undefined;\n return path.split(\".\").reduce((acc: unknown, part: string) => acc && (acc as Record<string, unknown>)[part], obj);\n}\n\nlet operationsRegistered = false;\n\n/**\n * Register custom JSON Logic operations for Rebase.\n * Call this once at app initialization.\n */\nexport function registerConditionOperations(): void {\n if (operationsRegistered) return;\n\n // Check if user has a specific role by ID\n jsonLogic.add_operation(\"hasRole\", function (this: ConditionContext, roleId: string) {\n return this?.user?.roles?.includes(roleId) ?? false;\n });\n\n // Check if user has any of the specified roles\n jsonLogic.add_operation(\"hasAnyRole\", function (this: ConditionContext, roleIds: string[]) {\n if (!this?.user?.roles || !Array.isArray(roleIds)) return false;\n return roleIds.some(role => this.user.roles.includes(role));\n });\n\n // Check if a timestamp is today\n jsonLogic.add_operation(\"isToday\", (timestamp: number) => {\n if (!timestamp) return false;\n const date = new Date(timestamp);\n const today = new Date();\n return date.getFullYear() === today.getFullYear() &&\n date.getMonth() === today.getMonth() &&\n date.getDate() === today.getDate();\n });\n\n // Check if a timestamp is in the past\n jsonLogic.add_operation(\"isPast\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp < Date.now();\n });\n\n // Check if a timestamp is in the future\n jsonLogic.add_operation(\"isFuture\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp > Date.now();\n });\n\n operationsRegistered = true;\n}\n\n/**\n * Evaluate a condition against the given context.\n *\n * A condition may be stated as a literal instead of a rule — `hidden: true`\n * rather than `hidden: { \"==\": [1, 1] }` — and a literal is already its own\n * answer, so it is returned rather than handed to the evaluator.\n */\nexport function evaluateCondition(rule: ConditionRule, context: ConditionContext): unknown {\n if (typeof rule === \"boolean\") return rule;\n // Ensure operations are registered\n registerConditionOperations();\n return jsonLogic.apply(rule, context);\n}\n\n/**\n * Convert a value to a format suitable for JSON Logic evaluation.\n * Specifically handles Date objects by converting them to Unix timestamps.\n */\nfunction serializeValueForConditions(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.getTime();\n }\n\n // Handle Firestore Timestamp-like objects (have toDate or toMillis)\n if (typeof (value as { toMillis?: () => number })?.toMillis === \"function\") {\n return (value as { toMillis: () => number }).toMillis();\n }\n if (typeof (value as { toDate?: () => Date })?.toDate === \"function\") {\n return (value as { toDate: () => Date }).toDate().getTime();\n }\n\n // Handle arrays recursively\n if (Array.isArray(value)) {\n return value.map(serializeValueForConditions);\n }\n\n // Handle plain objects recursively\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValueForConditions((value as Record<string, unknown>)[key]);\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Build a ConditionContext from the current property resolution context.\n */\nexport function buildConditionContext(params: {\n propertyKey?: string;\n values?: Record<string, unknown>;\n previousValues?: Record<string, unknown>;\n path: string;\n entityId?: string;\n index?: number;\n authController: AuthState;\n}): ConditionContext {\n const {\n propertyKey,\n values,\n previousValues,\n path,\n entityId,\n index,\n authController\n } = params;\n\n const user = authController.user;\n const serializedValues = serializeValueForConditions(values ?? {});\n const serializedPreviousValues = serializeValueForConditions(previousValues ?? values ?? {});\n\n return {\n values: serializedValues as Record<string, unknown>,\n previousValues: serializedPreviousValues as Record<string, unknown>,\n propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : undefined,\n path,\n entityId,\n isNew: !entityId,\n index,\n user: {\n uid: user?.uid ?? \"\",\n email: user?.email ?? null,\n displayName: user?.displayName ?? null,\n photoURL: user?.photoURL ?? null,\n roles: (user?.roles ?? []).map((r: unknown) => typeof r === \"string\" ? r : (r as { id: string }).id)\n },\n now: Date.now()\n };\n}\n\n/**\n * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.\n */\n"],"mappings":";;;;;AAiBA,SAAS,MAAM,KAAwC,MAAuB;CAC1E,IAAI,CAAC,OAAO,CAAC,MAAM,OAAO,KAAA;CAC1B,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,QAAQ,KAAc,SAAiB,OAAQ,IAAgC,OAAO,GAAG;AACpH;AAEA,IAAI,uBAAuB;;;;;AAM3B,SAAgB,8BAAoC;CAChD,IAAI,sBAAsB;CAG1B,UAAU,cAAc,WAAW,SAAkC,QAAgB;EACjF,OAAO,MAAM,MAAM,OAAO,SAAS,MAAM,KAAK;CAClD,CAAC;CAGD,UAAU,cAAc,cAAc,SAAkC,SAAmB;EACvF,IAAI,CAAC,MAAM,MAAM,SAAS,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO;EAC1D,OAAO,QAAQ,MAAK,SAAQ,KAAK,KAAK,MAAM,SAAS,IAAI,CAAC;CAC9D,CAAC;CAGD,UAAU,cAAc,YAAY,cAAsB;EACtD,IAAI,CAAC,WAAW,OAAO;EACvB,MAAM,OAAO,IAAI,KAAK,SAAS;EAC/B,MAAM,wBAAQ,IAAI,KAAK;EACvB,OAAO,KAAK,YAAY,MAAM,MAAM,YAAY,KAC5C,KAAK,SAAS,MAAM,MAAM,SAAS,KACnC,KAAK,QAAQ,MAAM,MAAM,QAAQ;CACzC,CAAC;CAGD,UAAU,cAAc,WAAW,cAAsB;EACrD,IAAI,CAAC,WAAW,OAAO;EACvB,OAAO,YAAY,KAAK,IAAI;CAChC,CAAC;CAGD,UAAU,cAAc,aAAa,cAAsB;EACvD,IAAI,CAAC,WAAW,OAAO;EACvB,OAAO,YAAY,KAAK,IAAI;CAChC,CAAC;CAED,uBAAuB;AAC3B;;;;;;;;AASA,SAAgB,kBAAkB,MAAqB,SAAoC;CACvF,IAAI,OAAO,SAAS,WAAW,OAAO;CAEtC,4BAA4B;CAC5B,OAAO,UAAU,MAAM,MAAM,OAAO;AACxC;;;;;AAMA,SAAS,4BAA4B,OAAyB;CAC1D,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC5B,OAAO;CAIX,IAAI,iBAAiB,MACjB,OAAO,MAAM,QAAQ;CAIzB,IAAI,OAAQ,OAAuC,aAAa,YAC5D,OAAQ,MAAqC,SAAS;CAE1D,IAAI,OAAQ,OAAmC,WAAW,YACtD,OAAQ,MAAiC,OAAO,CAAC,CAAC,QAAQ;CAI9D,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO,MAAM,IAAI,2BAA2B;CAIhD,IAAI,OAAO,UAAU,UAAU;EAC3B,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,GAC1D,OAAO,OAAO,4BAA6B,MAAkC,IAAI;EAErF,OAAO;CACX;CAEA,OAAO;AACX;;;;AAKA,SAAgB,sBAAsB,QAQjB;CACjB,MAAM,EACF,aACA,QACA,gBACA,MACA,UACA,OACA,mBACA;CAEJ,MAAM,OAAO,eAAe;CAC5B,MAAM,mBAAmB,4BAA4B,UAAU,CAAC,CAAC;CAGjE,OAAO;EACH,QAAQ;EACR,gBAJ6B,4BAA4B,kBAAkB,UAAU,CAAC,CAItE;EAChB,eAAe,cAAc,MAAM,kBAAkB,WAAW,IAAI,KAAA;EACpE;EACA;EACA,OAAO,CAAC;EACR;EACA,MAAM;GACF,KAAK,MAAM,OAAO;GAClB,OAAO,MAAM,SAAS;GACtB,aAAa,MAAM,eAAe;GAClC,UAAU,MAAM,YAAY;GAC5B,QAAQ,MAAM,SAAS,CAAC,EAAA,CAAG,KAAK,MAAe,OAAO,MAAM,WAAW,IAAK,EAAqB,EAAE;EACvG;EACA,KAAK,KAAK,IAAI;CAClB;AACJ"}
1
+ {"version":3,"file":"conditions.es.js","names":[],"sources":["../../src/util/conditions.ts"],"sourcesContent":["import jsonLogic from \"json-logic-js\";\nimport {\n ArrayProperty,\n AuthState,\n ConditionContext,\n ConditionRule,\n EnumValueConfig,\n NumberProperty,\n PropertyConditions,\n Property,\n ReferenceProperty,\n StringProperty\n} from \"@rebasepro/types\";\n\n/**\n * Access a nested property from an object via dot notation.\n */\nfunction getIn(obj: Record<string, unknown> | unknown, path: string): unknown {\n if (!obj || !path) return undefined;\n return path.split(\".\").reduce((acc: unknown, part: string) => acc && (acc as Record<string, unknown>)[part], obj);\n}\n\nlet operationsRegistered = false;\n\n/**\n * Register custom JSON Logic operations for Rebase.\n * Call this once at app initialization.\n */\nexport function registerConditionOperations(): void {\n if (operationsRegistered) return;\n\n // Check if user has a specific role by ID\n jsonLogic.add_operation(\"hasRole\", function (this: ConditionContext, roleId: string) {\n return this?.user?.roles?.includes(roleId) ?? false;\n });\n\n // Check if user has any of the specified roles\n jsonLogic.add_operation(\"hasAnyRole\", function (this: ConditionContext, roleIds: string[]) {\n if (!this?.user?.roles || !Array.isArray(roleIds)) return false;\n return roleIds.some(role => this.user.roles.includes(role));\n });\n\n // Check if a timestamp is today\n jsonLogic.add_operation(\"isToday\", (timestamp: number) => {\n if (!timestamp) return false;\n const date = new Date(timestamp);\n const today = new Date();\n return date.getFullYear() === today.getFullYear() &&\n date.getMonth() === today.getMonth() &&\n date.getDate() === today.getDate();\n });\n\n // Check if a timestamp is in the past\n jsonLogic.add_operation(\"isPast\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp < Date.now();\n });\n\n // Check if a timestamp is in the future\n jsonLogic.add_operation(\"isFuture\", (timestamp: number) => {\n if (!timestamp) return false;\n return timestamp > Date.now();\n });\n\n operationsRegistered = true;\n}\n\n/**\n * Evaluate a condition against the given context.\n *\n * A condition may be stated as a literal instead of a rule — `hidden: true`\n * rather than `hidden: { \"==\": [1, 1] }`, a bound as `min: 0` — and a literal\n * is already its own answer, so it is returned rather than handed to the\n * evaluator.\n */\nexport function evaluateCondition(rule: ConditionRule | number, context: ConditionContext): unknown {\n if (typeof rule === \"boolean\" || typeof rule === \"number\") return rule;\n // Ensure operations are registered\n registerConditionOperations();\n return jsonLogic.apply(rule, context);\n}\n\n/**\n * Convert a value to a format suitable for JSON Logic evaluation.\n * Specifically handles Date objects by converting them to Unix timestamps.\n */\nfunction serializeValueForConditions(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.getTime();\n }\n\n // Handle Firestore Timestamp-like objects (have toDate or toMillis)\n if (typeof (value as { toMillis?: () => number })?.toMillis === \"function\") {\n return (value as { toMillis: () => number }).toMillis();\n }\n if (typeof (value as { toDate?: () => Date })?.toDate === \"function\") {\n return (value as { toDate: () => Date }).toDate().getTime();\n }\n\n // Handle arrays recursively\n if (Array.isArray(value)) {\n return value.map(serializeValueForConditions);\n }\n\n // Handle plain objects recursively\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValueForConditions((value as Record<string, unknown>)[key]);\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Build a ConditionContext from the current property resolution context.\n */\nexport function buildConditionContext(params: {\n propertyKey?: string;\n values?: Record<string, unknown>;\n previousValues?: Record<string, unknown>;\n path: string;\n entityId?: string;\n index?: number;\n authController: AuthState;\n}): ConditionContext {\n const {\n propertyKey,\n values,\n previousValues,\n path,\n entityId,\n index,\n authController\n } = params;\n\n const user = authController.user;\n const serializedValues = serializeValueForConditions(values ?? {});\n const serializedPreviousValues = serializeValueForConditions(previousValues ?? values ?? {});\n\n return {\n values: serializedValues as Record<string, unknown>,\n previousValues: serializedPreviousValues as Record<string, unknown>,\n propertyValue: propertyKey ? getIn(serializedValues, propertyKey) : undefined,\n path,\n entityId,\n isNew: !entityId,\n index,\n user: {\n uid: user?.uid ?? \"\",\n email: user?.email ?? null,\n displayName: user?.displayName ?? null,\n photoURL: user?.photoURL ?? null,\n roles: (user?.roles ?? []).map((r: unknown) => typeof r === \"string\" ? r : (r as { id: string }).id)\n },\n now: Date.now()\n };\n}\n\n/**\n * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.\n */\n"],"mappings":";;;;;AAiBA,SAAS,MAAM,KAAwC,MAAuB;CAC1E,IAAI,CAAC,OAAO,CAAC,MAAM,OAAO,KAAA;CAC1B,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,QAAQ,KAAc,SAAiB,OAAQ,IAAgC,OAAO,GAAG;AACpH;AAEA,IAAI,uBAAuB;;;;;AAM3B,SAAgB,8BAAoC;CAChD,IAAI,sBAAsB;CAG1B,UAAU,cAAc,WAAW,SAAkC,QAAgB;EACjF,OAAO,MAAM,MAAM,OAAO,SAAS,MAAM,KAAK;CAClD,CAAC;CAGD,UAAU,cAAc,cAAc,SAAkC,SAAmB;EACvF,IAAI,CAAC,MAAM,MAAM,SAAS,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO;EAC1D,OAAO,QAAQ,MAAK,SAAQ,KAAK,KAAK,MAAM,SAAS,IAAI,CAAC;CAC9D,CAAC;CAGD,UAAU,cAAc,YAAY,cAAsB;EACtD,IAAI,CAAC,WAAW,OAAO;EACvB,MAAM,OAAO,IAAI,KAAK,SAAS;EAC/B,MAAM,wBAAQ,IAAI,KAAK;EACvB,OAAO,KAAK,YAAY,MAAM,MAAM,YAAY,KAC5C,KAAK,SAAS,MAAM,MAAM,SAAS,KACnC,KAAK,QAAQ,MAAM,MAAM,QAAQ;CACzC,CAAC;CAGD,UAAU,cAAc,WAAW,cAAsB;EACrD,IAAI,CAAC,WAAW,OAAO;EACvB,OAAO,YAAY,KAAK,IAAI;CAChC,CAAC;CAGD,UAAU,cAAc,aAAa,cAAsB;EACvD,IAAI,CAAC,WAAW,OAAO;EACvB,OAAO,YAAY,KAAK,IAAI;CAChC,CAAC;CAED,uBAAuB;AAC3B;;;;;;;;;AAUA,SAAgB,kBAAkB,MAA8B,SAAoC;CAChG,IAAI,OAAO,SAAS,aAAa,OAAO,SAAS,UAAU,OAAO;CAElE,4BAA4B;CAC5B,OAAO,UAAU,MAAM,MAAM,OAAO;AACxC;;;;;AAMA,SAAS,4BAA4B,OAAyB;CAC1D,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC5B,OAAO;CAIX,IAAI,iBAAiB,MACjB,OAAO,MAAM,QAAQ;CAIzB,IAAI,OAAQ,OAAuC,aAAa,YAC5D,OAAQ,MAAqC,SAAS;CAE1D,IAAI,OAAQ,OAAmC,WAAW,YACtD,OAAQ,MAAiC,OAAO,CAAC,CAAC,QAAQ;CAI9D,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO,MAAM,IAAI,2BAA2B;CAIhD,IAAI,OAAO,UAAU,UAAU;EAC3B,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAgC,GAC1D,OAAO,OAAO,4BAA6B,MAAkC,IAAI;EAErF,OAAO;CACX;CAEA,OAAO;AACX;;;;AAKA,SAAgB,sBAAsB,QAQjB;CACjB,MAAM,EACF,aACA,QACA,gBACA,MACA,UACA,OACA,mBACA;CAEJ,MAAM,OAAO,eAAe;CAC5B,MAAM,mBAAmB,4BAA4B,UAAU,CAAC,CAAC;CAGjE,OAAO;EACH,QAAQ;EACR,gBAJ6B,4BAA4B,kBAAkB,UAAU,CAAC,CAItE;EAChB,eAAe,cAAc,MAAM,kBAAkB,WAAW,IAAI,KAAA;EACpE;EACA;EACA,OAAO,CAAC;EACR;EACA,MAAM;GACF,KAAK,MAAM,OAAO;GAClB,OAAO,MAAM,SAAS;GACtB,aAAa,MAAM,eAAe;GAClC,UAAU,MAAM,YAAY;GAC5B,QAAQ,MAAM,SAAS,CAAC,EAAA,CAAG,KAAK,MAAe,OAAO,MAAM,WAAW,IAAK,EAAqB,EAAE;EACvG;EACA,KAAK,KAAK,IAAI;CAClB;AACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/common",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Rebase shared core — collection registry, data driver adapter and fluent query builder. No React dependency.",
5
5
  "keywords": [
6
6
  "rebase",
@@ -46,8 +46,8 @@
46
46
  "dependencies": {
47
47
  "fast-equals": "6.0.2",
48
48
  "json-logic-js": "^2.0.5",
49
- "@rebasepro/types": "0.26.0",
50
- "@rebasepro/utils": "0.26.0"
49
+ "@rebasepro/types": "0.27.0",
50
+ "@rebasepro/utils": "0.27.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jest/globals": "^30.4.1",