@rebasepro/common 0.12.0 → 0.12.1-canary.g06f263c

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/dist/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ANONYMOUS_USER_ID, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, getDataSourceCapabilities, getDeclaredSubcollections, isManyToMany, isPostgresCollectionConfig, isRelationalCollectionConfig, policy, toCanonicalOp } from "@rebasepro/types";
2
- import { deepClone, generateForeignKeyName, getIn, getPolicyOperations, isDefaultFieldConfigId, mergeDeep, prettifyIdentifier, randomString, removeFunctions, toSnakeCase } from "@rebasepro/utils";
1
+ import { ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, getDataSourceCapabilities, getDeclaredSubcollections, isAnonymousUid, isManyToMany, isPostgresCollectionConfig, isRelationalCollectionConfig, policy, toCanonicalOp } from "@rebasepro/types";
2
+ import { deepClone, generateForeignKeyName, getIn, getPolicyNamesForRules, getPolicyOperations, isDefaultFieldConfigId, mergeDeep, prettifyIdentifier, randomString, removeFunctions, toSnakeCase } from "@rebasepro/utils";
3
3
  import jsonLogic from "json-logic-js";
4
4
  import { deepEqual } from "fast-equals";
5
5
  //#region src/util/common.ts
@@ -957,7 +957,12 @@ var UID_NOT_NULL = /auth\.uid\(\)\s+IS\s+NOT\s+NULL/i;
957
957
  * is how the trusted *server* context is recognised), so:
958
958
  *
959
959
  * - `auth.uid() IS NOT NULL` is a tautology on the user path, and
960
- * - `auth.uid() != 'anon'` compares against a string no caller ever has.
960
+ * - `auth.uid() != 'anon'` excludes one spelling of anonymous and admits the
961
+ * other. This one is not hypothetical and was not only a foreign habit:
962
+ * rebase's own request path reported `'anon'` while everything that compiled
963
+ * or checked a policy used `'anonymous'`, so whichever literal an author
964
+ * picked, half the anonymous callers walked through. See
965
+ * {@link ANONYMOUS_USER_IDS}.
961
966
  *
962
967
  * Either one turns a lockdown into a full grant, and neither looks wrong. No
963
968
  * real user id is ever one of these literals, and a user-context request is
@@ -995,7 +1000,7 @@ function findAnonymousGrants(expr) {
995
1000
  found.push({
996
1001
  pattern: "foreign-uid-literal",
997
1002
  detail: literal.value,
998
- explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for every caller. Use \`condition: policy.authenticated()\` to mean "signed in".`
1003
+ explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for every caller. Use \`condition: policy.authenticated()\` to mean "signed in" — it compiles to NOT IN (${ANONYMOUS_USER_IDS.map((v) => `'${v}'`).join(", ")}), covering every spelling rebase has reported rather than whichever one you remember.`
999
1004
  });
1000
1005
  return;
1001
1006
  }
@@ -1090,7 +1095,7 @@ function compile(expr, scope) {
1090
1095
  }
1091
1096
  case "rolesOverlap": return `string_to_array(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;
1092
1097
  case "rolesContain": return `string_to_array(auth.roles(), ',') @> ${rolesArraySql(expr.roles)}`;
1093
- case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid() <> ${quoteLiteral(ANONYMOUS_USER_ID)}`;
1098
+ case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid() NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(", ")})`;
1094
1099
  case "serverContext": return "auth.uid() IS NULL";
1095
1100
  case "existsIn": return compileExistsIn(expr, scope);
1096
1101
  case "raw": return expr.sql.replace(/\{(\w+)\}/g, (_, col) => `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);
@@ -1187,7 +1192,7 @@ function evaluatePolicy(expr, ctx) {
1187
1192
  const userRoles = ctx.roles ?? [];
1188
1193
  return expr.roles.every((r) => r === "public" || userRoles.includes(r));
1189
1194
  }
1190
- case "authenticated": return ctx.uid != null && ctx.uid !== ANONYMOUS_USER_ID;
1195
+ case "authenticated": return ctx.uid != null && !isAnonymousUid(ctx.uid);
1191
1196
  case "serverContext": return false;
1192
1197
  case "existsIn": return "unknown";
1193
1198
  case "raw": return "unknown";
@@ -1653,6 +1658,26 @@ function getInjectedSecurityRules(collection) {
1653
1658
  const explicitCount = (collection.securityRules ?? []).length;
1654
1659
  return getEffectiveSecurityRules(collection).slice(explicitCount);
1655
1660
  }
1661
+ /**
1662
+ * Every policy name `rebase db push` would write for a collection.
1663
+ *
1664
+ * This is the answer to "did the codebase produce this live policy?", and it is
1665
+ * more than `securityRules.map(r => r.name)` for two reasons:
1666
+ *
1667
+ * - a rule without an explicit `name` compiles to `<table>_<op>_<hash>`, one
1668
+ * per operation, so comparing `rule.name` to `policyname` never matches it;
1669
+ * - the generator also injects the safe-by-default baseline
1670
+ * (`<table>_default_admin_*`), which is in no collection's `securityRules`.
1671
+ *
1672
+ * Every UI that flags drift has to get both right, and each one that derived it
1673
+ * by hand got a different subset — which is how four policies *Rebase itself
1674
+ * wrote* came to be badged as hand-written drift on every table in a project,
1675
+ * with a button offering to import them back into the codebase that produced
1676
+ * them. There is one derivation now, and this is it.
1677
+ */
1678
+ function getGeneratedPolicyNames(collection) {
1679
+ return getPolicyNamesForRules(getEffectiveSecurityRules(collection), getTableName(collection));
1680
+ }
1656
1681
  //#endregion
1657
1682
  //#region src/util/junction-policies.ts
1658
1683
  var SERVER_OR_ADMIN_EXPR = policy.or(policy.serverContext(), policy.rolesOverlap(["admin"]));
@@ -1952,7 +1977,7 @@ function buildConditionContext(params) {
1952
1977
  * Maps a PostgreSQL column data type to a Rebase property type.
1953
1978
  */
1954
1979
  function pgTypeToRebaseProperty(column) {
1955
- const { column_name, data_type, udt_name, is_nullable, column_default, enum_values } = column;
1980
+ const { column_name, data_type, udt_name, is_nullable, column_default, character_maximum_length, enum_values } = column;
1956
1981
  const required = is_nullable === "NO";
1957
1982
  const prettifiedName = prettifyIdentifier(column_name);
1958
1983
  const isAutoId = column_default != null && (column_default.includes("nextval") || column_default.includes("gen_random_uuid") || column_default.includes("uuid_generate") || column_default.includes("identity"));
@@ -1976,11 +2001,15 @@ function pgTypeToRebaseProperty(column) {
1976
2001
  let colType = "varchar";
1977
2002
  if (dt === "text" || dt === "citext") colType = "text";
1978
2003
  if (dt === "char" || dt === "character") colType = "char";
2004
+ const declaredLength = colType === "text" ? null : character_maximum_length;
1979
2005
  const prop = {
1980
2006
  type: "string",
1981
2007
  name: prettifiedName,
1982
2008
  columnType: colType,
1983
- validation: required ? { required: true } : void 0
2009
+ validation: required || declaredLength ? {
2010
+ ...required ? { required: true } : {},
2011
+ ...declaredLength ? { max: declaredLength } : {}
2012
+ } : void 0
1984
2013
  };
1985
2014
  if (isAutoId) prop.isId = "manual";
1986
2015
  return prop;
@@ -2184,6 +2213,34 @@ function buildCollectionFromTableMetadata(tableName, metadata) {
2184
2213
  };
2185
2214
  }
2186
2215
  //#endregion
2216
+ //#region src/util/string-column-length.ts
2217
+ /**
2218
+ * The length a bounded string column is declared with when the property does
2219
+ * not say. Historical: it is what the DDL generator hardcoded, kept so that
2220
+ * regenerating an existing schema does not silently redefine its columns.
2221
+ */
2222
+ var DEFAULT_STRING_COLUMN_LENGTH = 255;
2223
+ /**
2224
+ * How wide a `varchar`/`char` column should be for a given property.
2225
+ *
2226
+ * One definition, three call sites, because they used to disagree. For the same
2227
+ * `columnType: "varchar"` property the DDL generator emitted `VARCHAR(255)`
2228
+ * while the Drizzle generator emitted a bare `varchar("col")` — which Postgres
2229
+ * reads as *unbounded* — so which of the two you ran decided whether the column
2230
+ * had a limit at all. Introspection then dropped the length entirely, so reading
2231
+ * an existing `character varying(500)` column back and regenerating it produced
2232
+ * a `VARCHAR(255)`: a silent narrowing of a column with data already in it.
2233
+ *
2234
+ * `validation.max` is the property's own statement about how long the value may
2235
+ * be, so it is the only sensible source for the column's width — and it keeps
2236
+ * the constraint the database enforces in step with the one the app enforces,
2237
+ * rather than inventing a second, different limit underneath it.
2238
+ */
2239
+ function resolveStringColumnLength(prop) {
2240
+ const max = prop.validation?.max;
2241
+ return typeof max === "number" && Number.isInteger(max) && max > 0 ? max : 255;
2242
+ }
2243
+ //#endregion
2187
2244
  //#region src/data/resolveDataSource.ts
2188
2245
  /**
2189
2246
  * Build a keyed registry from a list of {@link DataSourceDefinition}s.
@@ -3773,6 +3830,6 @@ async function detectJunctionTables(executeSql) {
3773
3830
  return junctionTables;
3774
3831
  }
3775
3832
  //#endregion
3776
- export { COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, JUNCTION_TABLES_SQL, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, RebasePaginationError, and, buildCollection, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildProperty, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, defaultUsersCollection, defineCollection, deserializeFilter, deserializeLogicalCondition, deserializeOrderBy, detectJunctionTables, embedParentExpression, enumToObjectEntries, evaluateCondition, evaluatePolicy, findAnonymousGrants, findRelation, fullPathToCollectionSegments, getArrayResolvedProperties, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, getTableVarName, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, normalizeToEntityRelation, or, paginateFind, parseIdValues, policyToPostgres, registerConditionOperations, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeLogicalCondition, serializeOrderBy, sortProperties, sqlToPolicy, stripCollectionPath, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, wrapAsEntityData, wrapAsSdkData };
3833
+ export { COLLECTION_PATH_SEPARATOR, COMPOSITE_ID_SEPARATOR, CollectionRegistry, DEFAULT_FIND_ALL_MAX_ROWS, DEFAULT_MAX_PAGES, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, DEFAULT_PAGE_SIZE, DEFAULT_STRING_COLUMN_LENGTH, JUNCTION_TABLES_SQL, QueryBuilder, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, RebasePaginationError, and, buildCollection, buildCollectionFromTableMetadata, buildCompositeId, buildConditionContext, buildProperty, buildPropertyCallbacks, buildRebaseData, buildRoutedRebaseData, buildSdkData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, checkOperation, classifyTable, collectAllPages, cond, createDataSourceRegistry, createPaginationHelpers, createRelationRef, createRelationRefWithData, defaultUsersCollection, defineCollection, deserializeFilter, deserializeLogicalCondition, deserializeOrderBy, detectJunctionTables, embedParentExpression, enumToObjectEntries, evaluateCondition, evaluatePolicy, findAnonymousGrants, findRelation, fullPathToCollectionSegments, getArrayResolvedProperties, getColumnName, getDeclaredPrimaryKeys, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEffectiveSecurityRules, getEntityChildViews, getEnumVarName, getGeneratedPolicyNames, getInjectedSecurityRules, getJunctionCollectionConfig, getJunctionSecurityRules, getLabelOrConfigFrom, getPrimaryKeys, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, getTableVarName, isJunctionBackedRelation, isPropertyBuilder, isRebaseInternalTable, normalizeToEntityRelation, or, paginateFind, parseIdValues, policyToPostgres, registerConditionOperations, resolveArrayProperties, resolveCollectionRelations, resolveDataSource, resolveEnumValues, resolveJunctionSpecs, resolvePrimaryKeys, resolveProperties, resolveProperty, resolvePropertyEnum, resolveRelation, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, resolveStringColumnLength, sanitizeData, securityRuleToConditions, segmentsToStrippedPath, serializeFilter, serializeLogicalCondition, serializeOrderBy, sortProperties, sqlToPolicy, stripCollectionPath, traverseValueProperty, traverseValuesProperties, updateDateAutoValues, wrapAsEntityData, wrapAsSdkData };
3777
3834
 
3778
3835
  //# sourceMappingURL=index.es.js.map